Terminal program for MailStation devices
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}