/* See LICENSE for licence details. */ #if 0 void (*ctrl_func[CTRL_CHARS])(struct terminal_t *term) = { [BS] = bs, [HT] = tab, [LF] = nl, [VT] = nl, [FF] = nl, [CR] = cr, [ESC] = enter_esc, }; void (*esc_func[ESC_CHARS])(struct terminal_t *term) = { ['7'] = save_state, ['8'] = restore_state, ['D'] = nl, ['E'] = crnl, ['H'] = set_tabstop, ['M'] = reverse_nl, ['P'] = enter_dcs, ['Z'] = identify, ['['] = enter_csi, [']'] = enter_osc, ['c'] = ris, }; void (*csi_func[ESC_CHARS])(struct terminal_t *term, struct parm_t *) = { ['@'] = insert_blank, ['A'] = curs_up, ['B'] = curs_down, ['C'] = curs_forward, ['D'] = curs_back, ['E'] = curs_nl, ['F'] = curs_pl, ['G'] = curs_col, ['H'] = curs_pos, ['J'] = erase_display, ['K'] = erase_line, ['L'] = insert_line, ['M'] = delete_line, ['P'] = delete_char, ['X'] = erase_char, ['a'] = curs_forward, ['c'] = device_attribute, ['d'] = curs_line, ['e'] = curs_down, ['f'] = curs_pos, ['g'] = clear_tabstop, ['h'] = set_mode, ['l'] = reset_mode, ['m'] = set_attr, ['n'] = status_report, ['r'] = set_margin, /* XXX: not implemented because these sequences conflict DECSLRM/DECSHTS ['s'] = sco_save_state, ['u'] = sco_restore_state, */ ['`'] = curs_col, }; #endif /* ctr char/esc sequence/charset function */ void control_character(struct terminal_t *term, uint8_t ch) { static const char *ctrl_char[] = { "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS ", "HT ", "LF ", "VT ", "FF ", "CR ", "SO ", "SI ", "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM ", "SUB", "ESC", "FS ", "GS ", "RS ", "US ", }; *term->esc.bp = '\0'; logging(DEBUG, "ctl: %s\n", ctrl_char[ch]); switch(ch) { case BS: bs(term); break; case HT: tab(term); break; case LF: nl(term); break; case VT: nl(term); break; case FF: nl(term); break; case CR: cr(term); break; case ESC: enter_esc(term); break; default: break; } #if 0 if (ctrl_func[ch]) ctrl_func[ch](term); #endif } void esc_sequence(struct terminal_t *term, uint8_t ch) { *term->esc.bp = '\0'; logging(DEBUG, "esc: ESC %s\n", term->esc.buf); #if 0 if (strlen(term->esc.buf) == 1 && esc_func[ch]) esc_func[ch](term); #endif if (strlen(term->esc.buf) == 1) { switch(ch) { case '7': save_state(term); break; case '8': restore_state(term); break; case 'D': nl(term); break; case 'E': crnl(term); break; case 'H': set_tabstop(term); break; case 'M': reverse_nl(term); break; #if 0 case 'P': enter_dcs(term); break; #endif case 'Z': identify(term); break; case '[': enter_csi(term); break; case ']': enter_osc(term); break; case 'c': ris(term); break; default: break; } } /* not reset if csi/osc/dcs seqence */ if (ch == '[' || ch == ']' || ch == 'P') return; reset_esc(term); } void csi_sequence(struct terminal_t *term, uint8_t ch) { struct parm_t parm; *(term->esc.bp - 1) = '\0'; /* omit final character */ logging(DEBUG, "csi: CSI %s\n", term->esc.buf + 1); reset_parm(&parm); parse_arg(term->esc.buf + 1, &parm, ';', isdigit); /* skip '[' */ #if 0 if (csi_func[ch]) csi_func[ch](term, &parm); #endif switch (ch) { case '@': insert_blank(term, &parm); break; case 'A': curs_up(term, &parm); break; case 'B': curs_down(term, &parm); break; case 'C': curs_forward(term, &parm); break; case 'D': curs_back(term, &parm); break; case 'E': curs_nl(term, &parm); break; case 'F': curs_pl(term, &parm); break; case 'G': curs_col(term, &parm); break; case 'H': curs_pos(term, &parm); break; case 'J': erase_display(term, &parm); break; case 'K': erase_line(term, &parm); break; case 'L': insert_line(term, &parm); break; case 'M': delete_line(term, &parm); break; case 'P': delete_char(term, &parm); break; case 'X': erase_char(term, &parm); break; case 'a': curs_forward(term, &parm); break; case 'c': device_attribute(term, &parm);break; case 'd': curs_line(term, &parm); break; case 'e': curs_down(term, &parm); break; case 'f': curs_pos(term, &parm); break; case 'g': clear_tabstop(term, &parm); break; case 'h': set_mode(term, &parm); break; case 'l': reset_mode(term, &parm); break; case 'm': set_attr(term, &parm); break; case 'n': status_report(term, &parm); break; case 'r': set_margin(term, &parm); break; /* XXX: not implemented because these sequences conflict DECSLRM/DECSHTS case 's': sco_save_state(term, &parm); break; case 'u': sco_restore_state(term, &parm); break; */ case '`': curs_col(term, &parm); break; default: break; } reset_esc(term); } int is_osc_parm(int c) { if (isdigit(c) || isalpha(c) || c == '?' || c == ':' || c == '/' || c == '#') return true; else return false; } void omit_string_terminator(char *bp, uint8_t ch) { if (ch == BACKSLASH) /* ST: ESC BACKSLASH */ *(bp - 2) = '\0'; else /* ST: BEL */ *(bp - 1) = '\0'; } void osc_sequence(struct terminal_t *term, uint8_t ch) { int osc_mode; struct parm_t parm; omit_string_terminator(term->esc.bp, ch); logging(DEBUG, "osc: OSC %s\n", term->esc.buf); reset_parm(&parm); parse_arg(term->esc.buf + 1, &parm, ';', is_osc_parm); /* skip ']' */ if (parm.argc > 0) { osc_mode = dec2num(parm.argv[0]); logging(DEBUG, "osc_mode:%d\n", osc_mode); /* XXX: disable because this functions only work 24/32bpp -> support other bpp (including pseudo color) */ if (osc_mode == 4) set_palette(term, &parm); else if (osc_mode == 104) reset_palette(term, &parm); if (osc_mode == 8900) glyph_width_report(term, &parm); } reset_esc(term); } #if 0 void dcs_sequence(struct terminal_t *term, uint8_t ch) { char *cp; omit_string_terminator(term->esc.bp, ch); logging(DEBUG, "dcs: DCS %s\n", term->esc.buf); /* check DCS header */ cp = term->esc.buf + 1; /* skip P */ while (cp < term->esc.bp) { if (*cp == '{' || *cp == 'q') /* DECDLD or sixel */ break; else if (*cp == ';' || ('0' <= *cp && *cp <= '9')) /* valid DCS header */ ; else /* invalid sequence */ cp = term->esc.bp; cp++; } if (cp != term->esc.bp) { /* header only or couldn't find final char */ /* parse DCS header */ if (*cp == 'q') sixel_parse_header(term, term->esc.buf + 1); else if (*cp == '{') decdld_parse_header(term, term->esc.buf + 1); } reset_esc(term); } #endif void utf8_charset(struct terminal_t *term, uint8_t ch) { if (0x80 <= ch && ch <= 0xBF) { /* check illegal UTF-8 sequence * ? byte sequence: first byte must be between 0xC2 ~ 0xFD * 2 byte sequence: first byte must be between 0xC2 ~ 0xDF * 3 byte sequence: second byte following 0xE0 must be between 0xA0 ~ 0xBF * 4 byte sequence: second byte following 0xF0 must be between 0x90 ~ 0xBF * 5 byte sequence: second byte following 0xF8 must be between 0x88 ~ 0xBF * 6 byte sequence: second byte following 0xFC must be between 0x84 ~ 0xBF */ if ((term->charset.following_byte == 0) || (term->charset.following_byte == 1 && term->charset.count == 0 && term->charset.code <= 1) || (term->charset.following_byte == 2 && term->charset.count == 0 && term->charset.code == 0 && ch < 0xA0) || (term->charset.following_byte == 3 && term->charset.count == 0 && term->charset.code == 0 && ch < 0x90) || (term->charset.following_byte == 4 && term->charset.count == 0 && term->charset.code == 0 && ch < 0x88) || (term->charset.following_byte == 5 && term->charset.count == 0 && term->charset.code == 0 && ch < 0x84)) term->charset.is_valid = false; term->charset.code <<= 6; term->charset.code += ch & 0x3F; term->charset.count++; } else if (0xC0 <= ch && ch <= 0xDF) { term->charset.code = ch & 0x1F; term->charset.following_byte = 1; term->charset.count = 0; return; } else if (0xE0 <= ch && ch <= 0xEF) { term->charset.code = ch & 0x0F; term->charset.following_byte = 2; term->charset.count = 0; return; } else if (0xF0 <= ch && ch <= 0xF7) { term->charset.code = ch & 0x07; term->charset.following_byte = 3; term->charset.count = 0; return; } else if (0xF8 <= ch && ch <= 0xFB) { term->charset.code = ch & 0x03; term->charset.following_byte = 4; term->charset.count = 0; return; } else if (0xFC <= ch && ch <= 0xFD) { term->charset.code = ch & 0x01; term->charset.following_byte = 5; term->charset.count = 0; return; } else { /* 0xFE - 0xFF: not used in UTF-8 */ addch(term, REPLACEMENT_CHAR); reset_charset(term); return; } if (term->charset.count >= term->charset.following_byte) { /* illegal code point (ref: http://www.unicode.org/reports/tr27/tr27-4.html) 0xD800 ~ 0xDFFF : surrogate pair 0xFDD0 ~ 0xFDEF : noncharacter 0xnFFFE ~ 0xnFFFF: noncharacter (n: 0x00 ~ 0x10) 0x110000 ~ : invalid (unicode U+0000 ~ U+10FFFF) */ if (!term->charset.is_valid || (0xD800 <= term->charset.code && term->charset.code <= 0xDFFF) || (0xFDD0 <= term->charset.code && term->charset.code <= 0xFDEF) || ((term->charset.code & 0xFFFF) == 0xFFFE || (term->charset.code & 0xFFFF) == 0xFFFF) || (term->charset.code > 0x10FFFF)) addch(term, REPLACEMENT_CHAR); else addch(term, term->charset.code); reset_charset(term); } } void parse(struct terminal_t *term, uint8_t *buf, int size) { /* CTRL CHARS : 0x00 ~ 0x1F ASCII(printable): 0x20 ~ 0x7E CTRL CHARS(DEL) : 0x7F UTF-8 : 0x80 ~ 0xFF */ uint8_t ch; for (int i = 0; i < size; i++) { ch = buf[i]; if (term->esc.state == STATE_RESET) { /* interrupted by illegal byte */ if (term->charset.following_byte > 0 && (ch < 0x80 || ch > 0xBF)) { addch(term, REPLACEMENT_CHAR); reset_charset(term); } if (ch <= 0x1F) control_character(term, ch); else if (ch <= 0x7F) addch(term, ch); else utf8_charset(term, ch); } else if (term->esc.state == STATE_ESC) { if (push_esc(term, ch)) esc_sequence(term, ch); } else if (term->esc.state == STATE_CSI) { if (push_esc(term, ch)) csi_sequence(term, ch); } else if (term->esc.state == STATE_OSC) { if (push_esc(term, ch)) osc_sequence(term, ch); #if 0 } else if (term->esc.state == STATE_DCS) { if (push_esc(term, ch)) dcs_sequence(term, ch); #endif } } }