#include #include #include static void AddCharacter(int x, int y, int i, std::deque *lines) { while( static_cast(lines->size()) <= y ) lines->push_back(std::string()); while( static_cast((*lines)[y].size()) <= x ) (*lines)[y].push_back(' '); (*lines)[y][x] = i; } int main(int argc, char **argv) { std::deque lines; enum State { kNormal, kSeenEsc, kSeenEscBracket }; State state = kNormal; int param = 0; int x = 0; int y = 0; for(int i; (i = getchar()) != EOF;) { if( i == '\n' ) { y++; x = 0; state = kNormal; continue; } switch( state ) { case kNormal: if( i == '\x1b' ) { // Start of escape sequence state = kSeenEsc; } else { // Regular character. // // Assumes single-byte half-width characters. AddCharacter(x, y, i, &lines); x++; } break; case kSeenEsc: if( i == '[' ) { // Started escape sequence state = kSeenEscBracket; param = 0; } else { // Interrupted escape sequence AddCharacter(x, y, 0x1b, &lines); AddCharacter(x, y, '[', &lines); x += 2; } break; case kSeenEscBracket: if( i == ';' ) { // Ignore semicolons. These are used for text attribute // changes, but for our program we don't care. continue; } else if( i >= '0' && i <= '9' ) { // Parse parameter param = param * 10 + i - '0'; continue; } else if( i == 'A' ) { // Cursor up for(y -= param; y < 0; y++) lines.push_front(std::string()); } else if( i == 'B' ) { // Cursor down for(y += param; static_cast(lines.size()) + 1 < y;) lines.push_back(std::string()); } else if( i == 'C' ) { // Cursor forward x += param; } else if( i == 'D' ) { // Cursor back x -= param; if( x < 0 ) x = 0; } // Exit escape sequence state = kNormal; break; } } for(const std::string &i : lines) puts(i.c_str()); return 0; }