Terminal program for MailStation devices
at master 350 lines 7.8 kB view raw
1/* 2 * msTERM 3 * ANSI CSI parser 4 * 5 * Copyright (c) 2019 joshua stein <jcs@jcs.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <stdio.h> 21#include <string.h> 22#include <stdlib.h> 23 24#include "mailstation.h" 25 26unsigned char csibuf[TEXT_COLS]; 27unsigned int csibuflen; 28 29unsigned char in_csi; 30 31void 32parseCSI(void) 33{ 34 int x, y, serviced; 35 int param1 = -1, param2 = -1; 36 char c = csibuf[csibuflen - 1]; 37 char parambuf[4]; 38 int parambuflen; 39#ifdef DEBUG 40 char sb[TEXT_COLS]; 41#endif 42 43 if (c < 'A' || (c > 'Z' && c < 'a') || c > 'z') 44 return; 45 46 switch (c) { 47 case 'A': 48 case 'B': 49 case 'C': 50 case 'D': 51 case 'E': 52 case 'F': 53 case 'G': 54 case 'J': 55 case 'K': 56 case 'S': 57 case 'T': 58 case 'd': 59 case 'g': 60 case 's': 61 case 'u': 62 case '7': 63 case '8': 64 /* optional multiplier */ 65 if (c == 'J' || c == 'K') 66 param1 = 0; 67 else 68 param1 = 1; 69 70 if (csibuflen > 1) { 71 for (x = 0; x < csibuflen - 1, x < 4; x++) { 72 parambuf[x] = csibuf[x]; 73 parambuf[x + 1] = '\0'; 74 } 75 param1 = atoi(parambuf); 76 } 77 break; 78 case 'H': 79 case 'f': 80 /* two optional parameters separated by ; each defaulting to 1 */ 81 param1 = 1; 82 param2 = 1; 83 84 y = -1; 85 for (x = 0; x < csibuflen; x++) { 86 if (csibuf[x] == ';') { 87 y = x; 88 break; 89 } 90 } 91 if (y == -1) 92 /* CSI 17H -> CSI 17; */ 93 y = csibuflen - 1; 94 95 if (y > 0) { 96 for (x = 0; x < y && x < 4; x++) { 97 parambuf[x] = csibuf[x]; 98 parambuf[x + 1] = '\0'; 99 } 100 param1 = atoi(parambuf); 101 102 if (y < csibuflen - 2) { 103 parambuf[0] = '\0'; 104 for (x = 0; x < (csibuflen - 1 - y) && x < 4; x++) { 105 parambuf[x] = csibuf[y + 1 + x]; 106 parambuf[x + 1] = '\0'; 107 } 108 param2 = atoi(parambuf); 109 } 110 } 111 break; 112 } 113 114 serviced = 1; 115 116 uncursor(); 117 118#ifdef DEBUG 119 sprintf(sb, "CSI:"); 120 for (x = 0; x < csibuflen; x++) 121 sprintf(sb, "%s%c", sb, csibuf[x]); 122 sprintf(sb, "%s params:%d,%d", sb, param1, param2); 123 update_statusbar(sb); 124#endif 125 126 switch (c) { 127 case 'A': /* CUU - cursor up, stop at top of screen */ 128 for (x = 0; x < param1 && cursory > 0; x++) 129 cursory--; 130 break; 131 case 'B': /* CUD - cursor down, stop at bottom of screen */ 132 for (x = 0; x < param1 && cursory < TEXT_ROWS - 1; x++) 133 cursory++; 134 break; 135 case 'C': /* CUF - cursor forward, stop at screen edge */ 136 for (x = 0; x < param1 && cursorx < TEXT_COLS; x++) 137 cursorx++; 138 break; 139 case 'D': /* CUB - cursor back, stop at left edge of screen */ 140 for (x = 0; x < param1 && cursorx > 0; x++) 141 cursorx--; 142 break; 143 case 'E': /* CNL - cursor next line */ 144 cursorx = 0; 145 for (x = 0; x < param1 && cursory < TEXT_ROWS - 1; x++) 146 cursory++; 147 break; 148 case 'F': /* CPL - cursor previous line */ 149 cursorx = 0; 150 for (x = 0; x < param1 && cursory > 0; x++) 151 cursory--; 152 break; 153 case 'G': /* CHA - cursor horizontal absolute */ 154 if (param1 > TEXT_COLS) 155 param1 = TEXT_COLS; 156 cursorx = param1; 157 break; 158 case 'H': /* CUP - cursor absolute position */ 159 case 'f': /* HVP - horizontal vertical position */ 160 if (param1 - 1 < 0) 161 cursory = 0; 162 else if (param1 > TEXT_ROWS) 163 cursory = TEXT_ROWS - 1; 164 else 165 cursory = param1 - 1; 166 167 if (param2 - 1 < 0) 168 cursorx = 0; 169 else if (param2 > TEXT_COLS) 170 cursorx = TEXT_COLS - 1; 171 else 172 cursorx = param2 - 1; 173 break; 174 case 'J': /* ED - erase in display */ 175 switch (param1) { 176 case 0: 177 /* clear from cursor to end of screen */ 178 for (y = cursory; y < TEXT_ROWS; y++) { 179 for (x = 0; x < TEXT_COLS; x++) { 180 if (y == cursory && x < cursorx) 181 continue; 182 183 putchar_attr(y, x, ' ', 0); 184 } 185 } 186 break; 187 case 1: 188 /* clear from cursor to beginning of the screen */ 189 for (y = cursory; y >= 0; y--) { 190 for (x = TEXT_COLS; x >= 0; x--) { 191 if (y == cursory && x > cursorx) 192 continue; 193 194 putchar_attr(y, x, ' ', 0); 195 } 196 } 197 break; 198 case 2: 199 /* clear entire screen */ 200 for (y = 0; y < TEXT_ROWS; y++) { 201 for (x = 0; x < TEXT_COLS; x++) 202 putchar_attr(y, x, ' ', 0); 203 } 204 } 205 break; 206 case 'K': /* EL - erase in line */ 207 switch (param1) { 208 case 0: 209 /* clear from cursor to end of line */ 210 if (cursorx >= TEXT_COLS) 211 break; 212 for (x = cursorx; x < TEXT_COLS; x++) 213 putchar_attr(cursory, x, ' ', 0); 214 break; 215 case 1: 216 /* clear from cursor to beginning of line */ 217 if (cursorx == 0) 218 break; 219 for (x = cursorx; x >= 0; x--) 220 putchar_attr(cursory, x, ' ', 0); 221 break; 222 case 2: 223 /* clear entire line */ 224 for (x = 0; x < TEXT_COLS - 1; x++) 225 putchar_attr(cursory, x, ' ', 0); 226 } 227 break; 228 case 'S': /* SU - scroll up */ 229 /* TODO */ 230 break; 231 case 'T': /* SD - scroll down */ 232 /* TODO */ 233 break; 234 case 'd': /* absolute line number */ 235 if (param1 < 1) 236 cursory = 0; 237 else if (param1 > TEXT_ROWS) 238 cursory = TEXT_ROWS; 239 else 240 cursory = param1 - 1; 241 break; 242 case 'g': /* clear tabs, ignore */ 243 break; 244 case 'h': /* reset, ignore */ 245 break; 246 case 'm': /* graphic changes */ 247 parambuf[0] = '\0'; 248 parambuflen = 0; 249 param2 = putchar_sgr; 250 251 for (x = 0; x < csibuflen; x++) { 252 /* all the way to csibuflen to catch 'm' */ 253 if (csibuf[x] == ';' || csibuf[x] == 'm') { 254 param1 = atoi(parambuf); 255 256 switch (param1) { 257 case 0: /* reset */ 258 case 22: /* normal color */ 259 param2 = 0; 260 break; 261 case 1: /* bold */ 262 param2 |= ATTR_BOLD; 263 break; 264 case 4: /* underline */ 265 param2 |= ATTR_UNDERLINE; 266 break; 267 case 7: /* reverse */ 268 param2 |= ATTR_REVERSE; 269 break; 270 case 21: /* bold off */ 271 param2 &= ~(ATTR_BOLD); 272 break; 273 case 24: /* underline off */ 274 param2 &= ~(ATTR_UNDERLINE); 275 break; 276 case 27: /* inverse off */ 277 param2 &= ~(ATTR_REVERSE); 278 break; 279 } 280 281 parambuf[0] = '\0'; 282 parambuflen = 0; 283 } else if (parambuflen < 4) { 284 parambuf[parambuflen] = csibuf[x]; 285 parambuflen++; 286 parambuf[parambuflen] = '\0'; 287 } 288 } 289 290 putchar_sgr = param2; 291 292 break; 293 case 'n': /* DSR - device status report */ 294 switch (param1) { 295 case 5: /* terminal is ready */ 296 obuf[obuf_pos++] = ESC; 297 obuf[obuf_pos++] = '['; 298 obuf[obuf_pos++] = '0'; 299 obuf[obuf_pos++] = 'n'; 300 break; 301 case 6: /* CPR - report cursor position */ 302 obuf[obuf_pos++] = ESC; 303 obuf[obuf_pos++] = '['; 304 305 itoa(cursory + 1, parambuf, 10); 306 for (x = 0; x < sizeof(parambuf); x++) { 307 if (parambuf[x] == '\0') 308 break; 309 obuf[obuf_pos++] = parambuf[x]; 310 } 311 obuf[obuf_pos++] = ';'; 312 313 itoa(cursorx + 1, parambuf, 10); 314 for (x = 0; x < sizeof(parambuf); x++) { 315 if (parambuf[x] == '\0') 316 break; 317 obuf[obuf_pos++] = parambuf[x]; 318 } 319 320 obuf[obuf_pos++] = 'R'; 321 break; 322 } 323 break; 324 case '7': /* DECSC - save cursor */ 325 case 's': /* from ANSI.SYS */ 326 saved_cursorx = cursorx; 327 saved_cursory = cursory; 328 break; 329 case '8': /* DECRC - restore cursor */ 330 case 'u': /* from ANSI.SYS */ 331 cursorx = saved_cursorx; 332 cursory = saved_cursory; 333 break; 334 default: 335 /* 336 * if the last character is a letter and we haven't serviced 337 * it, assume it's a sequence we don't support and should just 338 * suppress 339 */ 340 if (c < 65 || (c > 90 && c < 97) || c > 122) 341 serviced = 0; 342 } 343 344 if (serviced) { 345 recursor(); 346 csibuflen = 0; 347 csibuf[0] = '\0'; 348 in_csi = 0; 349 } 350}