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
38static 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) */
58static 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
65void __ansi_init(const struct term_info *ti)
66{
67    memcpy(ti->ts, &default_state, sizeof default_state);
68}
69
70void __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