• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
4  *
5  *   Permission is hereby granted, free of charge, to any person
6  *   obtaining a copy of this software and associated documentation
7  *   files (the "Software"), to deal in the Software without
8  *   restriction, including without limitation the rights to use,
9  *   copy, modify, merge, publish, distribute, sublicense, and/or
10  *   sell copies of the Software, and to permit persons to whom
11  *   the Software is furnished to do so, subject to the following
12  *   conditions:
13  *
14  *   The above copyright notice and this permission notice shall
15  *   be included in all copies or substantial portions of the Software.
16  *
17  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24  *   OTHER DEALINGS IN THE SOFTWARE.
25  *
26  * ----------------------------------------------------------------------- */
27 
28 /*
29  * ansi.c
30  *
31  * ANSI character code engine
32  */
33 
34 #include <string.h>
35 #include <colortbl.h>
36 #include "ansi.h"
37 
38 static const struct term_state default_state = {
39     .state = st_init,
40     .pvt = false,
41     .nparms = 0,
42     .xy = {0, 0},
43     .cindex = 0,		/* First color table entry */
44     .vtgraphics = false,
45     .intensity = 1,
46     .underline = false,
47     .blink = false,
48     .reverse = false,
49     .fg = 7,
50     .bg = 0,
51     .autocr = true,	  	/* Mimic \n -> \r\n conversion by default */
52     .autowrap = true,		/* Wrap lines by default */
53     .saved_xy = {0, 0},
54     .cursor = true,
55 };
56 
57 /* DEC VT graphics to codepage 437 table (characters 0x60-0x7F only) */
58 static const char decvt_to_cp437[] = {
59     0004, 0261, 0007, 0007, 0007, 0007, 0370, 0361,
60     0007, 0007, 0331, 0277, 0332, 0300, 0305, 0304,
61     0304, 0304, 0137, 0137, 0303, 0264, 0301, 0302,
62     0263, 0363, 0362, 0343, 0330, 0234, 0007, 00
63 };
64 
__ansi_init(const struct term_info * ti)65 void __ansi_init(const struct term_info *ti)
66 {
67     memcpy(ti->ts, &default_state, sizeof default_state);
68 }
69 
__ansi_putchar(const struct term_info * ti,uint8_t ch)70 void __ansi_putchar(const struct term_info *ti, uint8_t ch)
71 {
72     const struct ansi_ops *op = ti->op;
73     struct term_state *st = ti->ts;
74     const int rows = ti->rows;
75     const int cols = ti->cols;
76     struct curxy xy = st->xy;
77 
78     switch (st->state) {
79     case st_init:
80 	switch (ch) {
81 	case 1 ... 5:
82 	    st->state = st_tbl;
83 	    st->parms[0] = ch;
84 	    break;
85 	case '\a':
86 	    op->beep();
87 	    break;
88 	case '\b':
89 	    if (xy.x > 0)
90 		xy.x--;
91 	    break;
92 	case '\t':
93 	    {
94 		int nsp = 8 - (xy.x & 7);
95 		while (nsp--)
96 		    __ansi_putchar(ti, ' ');
97 	    }
98 	    return;		/* Cursor already updated */
99 	case '\n':
100 	case '\v':
101 	case '\f':
102 	    xy.y++;
103 	    if (st->autocr)
104 		xy.x = 0;
105 	    break;
106 	case '\r':
107 	    xy.x = 0;
108 	    break;
109 	case 127:
110 	    /* Ignore delete */
111 	    break;
112 	case 14:
113 	    st->vtgraphics = 1;
114 	    break;
115 	case 15:
116 	    st->vtgraphics = 0;
117 	    break;
118 	case 27:
119 	    st->state = st_esc;
120 	    break;
121 	default:
122 	    /* Print character */
123 	    if (ch >= 32) {
124 		if (st->vtgraphics && (ch & 0xe0) == 0x60)
125 		    ch = decvt_to_cp437[ch - 0x60];
126 
127 		op->write_char(xy.x, xy.y, ch, st);
128 		xy.x++;
129 	    }
130 	    break;
131 	}
132 	break;
133 
134     case st_esc:
135 	switch (ch) {
136 	case '%':
137 	case '(':
138 	case ')':
139 	case '#':
140 	    /* Ignore this plus the subsequent character, allows
141 	       compatibility with Linux sequence to set charset */
142 	    break;
143 	case '[':
144 	    st->state = st_csi;
145 	    st->nparms = 0;
146 	    st->pvt = false;
147 	    memset(st->parms, 0, sizeof st->parms);
148 	    break;
149 	case 'c':
150 	    /* Reset terminal */
151 	    memcpy(&st, &default_state, sizeof st);
152 	    op->erase(st, 0, 0, cols - 1, rows - 1);
153 	    xy.x = xy.y = 0;
154 	    st->state = st_init;
155 	    break;
156 	default:
157 	    /* Ignore sequence */
158 	    st->state = st_init;
159 	    break;
160 	}
161 	break;
162 
163     case st_csi:
164 	{
165 	    int p0 = st->parms[0] ? st->parms[0] : 1;
166 
167 	    if (ch >= '0' && ch <= '9') {
168 		st->parms[st->nparms] = st->parms[st->nparms] * 10 + (ch - '0');
169 	    } else if (ch == ';') {
170 		st->nparms++;
171 		if (st->nparms >= ANSI_MAX_PARMS)
172 		    st->nparms = ANSI_MAX_PARMS - 1;
173 		break;
174 	    } else if (ch == '?') {
175 		st->pvt = true;
176 	    } else {
177 		switch (ch) {
178 		case 'A':
179 		    {
180 			int y = xy.y - p0;
181 			xy.y = (y < 0) ? 0 : y;
182 		    }
183 		    break;
184 		case 'B':
185 		    {
186 			int y = xy.y + p0;
187 			xy.y = (y >= rows) ? rows - 1 : y;
188 		    }
189 		    break;
190 		case 'C':
191 		    {
192 			int x = xy.x + p0;
193 			xy.x = (x >= cols) ? cols - 1 : x;
194 		    }
195 		    break;
196 		case 'D':
197 		    {
198 			int x = xy.x - p0;
199 			xy.x = (x < 0) ? 0 : x;
200 		    }
201 		    break;
202 		case 'E':
203 		    {
204 			int y = xy.y + p0;
205 			xy.y = (y >= rows) ? rows - 1 : y;
206 			xy.x = 0;
207 		    }
208 		    break;
209 		case 'F':
210 		    {
211 			int y = xy.y - p0;
212 			xy.y = (y < 0) ? 0 : y;
213 			xy.x = 0;
214 		    }
215 		    break;
216 		case 'G':
217 		case '\'':
218 		    {
219 			int x = st->parms[0] - 1;
220 			xy.x = (x >= cols) ? cols - 1 : (x < 0) ? 0 : x;
221 		    }
222 		    break;
223 		case 'H':
224 		case 'f':
225 		    {
226 			int y = st->parms[0] - 1;
227 			int x = st->parms[1] - 1;
228 
229 			xy.x = (x >= cols) ? cols - 1 : (x < 0) ? 0 : x;
230 			xy.y = (y >= rows) ? rows - 1 : (y < 0) ? 0 : y;
231 		    }
232 		    break;
233 		case 'J':
234 		    {
235 			switch (st->parms[0]) {
236 			case 0:
237 			    op->erase(st, xy.x, xy.y, cols - 1, xy.y);
238 			    if (xy.y < rows - 1)
239 				op->erase(st, 0, xy.y + 1, cols - 1, rows - 1);
240 			    break;
241 
242 			case 1:
243 			    if (xy.y > 0)
244 				op->erase(st, 0, 0, cols - 1, xy.y - 1);
245 			    if (xy.y > 0)
246 				op->erase(st, 0, xy.y, xy.x - 1, xy.y);
247 			    break;
248 
249 			case 2:
250 			    op->erase(st, 0, 0, cols - 1, rows - 1);
251 			    break;
252 
253 			default:
254 			    /* Ignore */
255 			    break;
256 			}
257 		    }
258 		    break;
259 		case 'K':
260 		    {
261 			switch (st->parms[0]) {
262 			case 0:
263 			    op->erase(st, xy.x, xy.y, cols - 1, xy.y);
264 			    break;
265 
266 			case 1:
267 			    if (xy.x > 0)
268 				op->erase(st, 0, xy.y, xy.x - 1, xy.y);
269 			    break;
270 
271 			case 2:
272 			    op->erase(st, 0, xy.y, cols - 1, xy.y);
273 			    break;
274 
275 			default:
276 			    /* Ignore */
277 			    break;
278 			}
279 		    }
280 		    break;
281 		case 'h':
282 		case 'l':
283 		{
284 		    bool set = (ch == 'h');
285 		    switch (st->parms[0]) {
286 		    case 7:	/* DECAWM */
287 			st->autowrap = set;
288 			break;
289 		    case 20:	/* LNM */
290 			st->autocr = set;
291 			break;
292 		    case 25:	/* DECTECM */
293 			st->cursor = set;
294 			op->showcursor(st);
295 			break;
296 		    default:
297 			/* Ignore */
298 			break;
299 		    }
300 		    break;
301 		}
302 		case 'm':
303 		    {
304 			static const int ansi2pc[8] =
305 			    { 0, 4, 2, 6, 1, 5, 3, 7 };
306 
307 			int i;
308 			for (i = 0; i <= st->nparms; i++) {
309 			    int a = st->parms[i];
310 			    switch (a) {
311 			    case 0:
312 				st->fg = 7;
313 				st->bg = 0;
314 				st->intensity = 1;
315 				st->underline = 0;
316 				st->blink = 0;
317 				st->reverse = 0;
318 				break;
319 			    case 1:
320 				st->intensity = 2;
321 				break;
322 			    case 2:
323 				st->intensity = 0;
324 				break;
325 			    case 4:
326 				st->underline = 1;
327 				break;
328 			    case 5:
329 				st->blink = 1;
330 				break;
331 			    case 7:
332 				st->reverse = 1;
333 				break;
334 			    case 21:
335 			    case 22:
336 				st->intensity = 1;
337 				break;
338 			    case 24:
339 				st->underline = 0;
340 				break;
341 			    case 25:
342 				st->blink = 0;
343 				break;
344 			    case 27:
345 				st->reverse = 0;
346 				break;
347 			    case 30 ... 37:
348 				st->fg = ansi2pc[a - 30];
349 				break;
350 			    case 38:
351 				st->fg = 7;
352 				st->underline = 1;
353 				break;
354 			    case 39:
355 				st->fg = 7;
356 				st->underline = 0;
357 				break;
358 			    case 40 ... 47:
359 				st->bg = ansi2pc[a - 40];
360 				break;
361 			    case 49:
362 				st->bg = 7;
363 				break;
364 			    default:
365 				/* Do nothing */
366 				break;
367 			    }
368 			}
369 		    }
370 		    break;
371 		case 's':
372 		    st->saved_xy = xy;
373 		    break;
374 		case 'u':
375 		    xy = st->saved_xy;
376 		    break;
377 		default:	/* Includes CAN and SUB */
378 		    break;	/* Drop unknown sequence */
379 		}
380 		st->state = st_init;
381 	    }
382 	}
383 	break;
384 
385     case st_tbl:
386 	st->parms[1] = 0;
387 	if (ch == '#')
388 	    st->state = st_tblc;
389 	else
390 	    st->state = st_init;
391 	break;
392 
393     case st_tblc:
394 	{
395 	    unsigned int n = (unsigned char)ch - '0';
396 	    const char *p;
397 
398 	    if (n < 10) {
399 		st->parms[1] = st->parms[1] * 10 + n;
400 
401 		if (!--st->parms[0]) {
402 		    if (st->parms[1] < console_color_table_size) {
403 			/* Set the color table index */
404 			st->cindex = st->parms[1];
405 
406 			/* See if there are any other attributes we care about */
407 			p = console_color_table[st->parms[1]].ansi;
408 			if (p) {
409 			    st->state = st_esc;
410 			    __ansi_putchar(ti, '[');
411 			    __ansi_putchar(ti, '0');
412 			    __ansi_putchar(ti, ';');
413 			    while (*p)
414 				__ansi_putchar(ti, *p++);
415 			    __ansi_putchar(ti, 'm');
416 			}
417 		    }
418 		    st->state = st_init;
419 		}
420 	    } else {
421 		st->state = st_init;
422 	    }
423 	}
424 	break;
425     }
426 
427     /* If we fell off the end of the screen, adjust */
428     if (xy.x >= cols) {
429 	if (st->autowrap) {
430 	    xy.x = 0;
431 	    xy.y++;
432 	} else {
433 	    xy.x = cols - 1;
434 	}
435     }
436     while (xy.y >= rows) {
437 	xy.y--;
438 	op->scroll_up(st);
439     }
440 
441     /* Update cursor position */
442     op->set_cursor(xy.x, xy.y, st->cursor);
443     st->xy = xy;
444 }
445