Home | History | Annotate | Download | only in sys
      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 
     65 void __ansi_init(const struct term_info *ti)
     66 {
     67     memcpy(ti->ts, &default_state, sizeof default_state);
     68 }
     69 
     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