//Types of ANSI Escape Sequences //C0 [0x07-0x0A|0x0C-0x0D|0x1B] //C1/Fe [ESC][0x40-0x5F] // technically can be represented in 1 byte w/ bytes above 0x80 but // unimplemented b/c of unicode // STring Terminator ESC \ //CSI (ESC\[|0x9B)[0-9:;<=>?]*[!"#$%&'()*+,-./]*[&A-Z[\]^_`a-z{|}~] //OSC (ESC\]|0x9D).*(0x9C|0x1B 0x5C) //OCS (ESCP|0x90).*(0x9C|0x1B 0x5C) //Fs ESC[0x60-0x7E] //nF ESC[0x20-02F]*[0x30-0x7E] //Fp ESC[0x30-0x3F] save and clear //Clearing commands //ESC c //ESC [ [1-3] J //ESCMA-49 mode switch bullshit that may or may not clear the screen // ESC [ ? [0-9]* [lh] //ESC [ [1-2] K //ESC [ m @ // note: these two can't clear im preview b/c of scroll locking //last read key enum esc_state { //C0 except BEL or ESC, \n ,\r and \f C0, ESC, CSI_START, CSI_MID, CSI_END, //OSC, DCS, PM APC etc ST terminted C1, C1_START, ST, nF_START, nF_END, //other fixed elngth C1, Fs, C1, //save and clear DECSC, DECRC, NORMAL, //escape followed by an escape, has to be user generated DOUBLE_ESC, }; //returns 1 if the current state is safe to insert characters after, 0 if not int safe_state(enum esc_state state) { switch (state) { case CSI_END: case ST: case nF_END: case C0: case C1: case DECSC: case DECRC: case DOUBLE_ESC: case NORMAL: return 1; break; default: return 0; break; } } //global stdin -> pty ESC state enum esc_state in_state = NORMAL; //global pty -> stdout ESC state enum esc_state out_state = NORMAL; //global output parity int out_c_parity = 0; //global output clear state, cleared by im_routine_update int out_clr = 0; //updates the state, the return is the type of the last character read //updates c parity depending on cursor store and restore //if c parity is not NULL // only from ESC 7 and ESC 8, any terminal that implements the CSI // ESC [ s and ESC [ u probably doesn't use the same buffer as above //sets clr to 1 if it encounters a cursor clear escape sequence // if clr is not NULL enum esc_state update_esc_state(char * buff,unsigned int buff_len, enum esc_state state, int * c_parity, int * clr) { for (int i = 0; i < buff_len; i++) { char c = buff[i]; switch (state) { case CSI_END: case ST: case nF_END: case C0: case C1: case DECSC: case DECRC: case DOUBLE_ESC: //all the sequences that have ended don't have an end //and default to normal case NORMAL: if (c >= 0x08 && c <= 0x0A) { state = C0; } if (c == 0x1B) { state = ESC; } else { //return to normal state = NORMAL; } break; case ESC: if (c == 'c') { if (clr != NULL) { *clr = 1; } } //fix length C1 else if (c == 'N' || c == 'O') { state = C1; } else if (c == '[') { state = CSI_START; } //ST terminated C1 OSC, DCS, SOS, PM, APC else if (c == ']' || c =='P' || c == '^' || c == 'X' || c == '_') { state = C1_START; } else if (c == '\\') { state - ST; } else if (c == '7') { state = DECSC; if ( c_parity != NULL) { *c_parity++; } } else if (c == '8') { state = DECRC; if ( c_parity != NULL) { *c_parity--; } } //nF else if (c >= 0x20 && c <= 0x2F) { state = nF_START; } //all other Fe/C1 Sequences, should be fixed len else if (c >= 0x40 && c <= 0x5F) { state = C1; } //Fs Sequences else if (c >= 0x60 && c <= 0x7E) { state = C1; } else if (c == 0x1B) { state = DOUBLE_ESC; //deformed escape sequence } else { state = NORMAL; } break; case C1_START: if (c == 0x1B) { state = ESC; } if (c == 0x9C) { state = ST; } break; case CSI_START: case CSI_MID: if (c == 'J' || c == 'h' || c == 'l') { state = CSI_END; if (clr != NULL) { *clr = 1; } } else if (c >= 0x30 && c <= 0x3F) { state = CSI_MID; } else if (c >= 0x20 && c <= 0x2F) { state = CSI_MID; } else if (c >= 0x40 && c <= 0x7E) { state = CSI_END; } else { //malformed CSI sequence state = NORMAL; } break; case nF_START: if (c >= 0x20 && c <= 0x2F) { state = nF_START; } else if (c >= 0x30 && c <= 0x7E) { state = nF_END; //malformed nF sequence } else { state = NORMAL; } break; } } return state; }