#include #include /* Global buffer of characters */ typedef struct { int x, y; /* Character position */ int code; /* Character code point */ } Char; static Char *characters = NULL; static int character_count = 0; static int character_capacity = 1; /* Add a single character to global buffer */ static void AddChar(int x, int y, int code) { if( character_count + 1 >= character_capacity ) { character_capacity *= 2; characters = (Char*)realloc(characters, character_capacity * sizeof(Char)); if( characters == NULL ) { fputs("Out of memory\n", stderr); exit(EXIT_FAILURE); } } characters[character_count].x = x; characters[character_count].y = y; characters[character_count].code = code; character_count++; } /* Read UTF-8 bytes */ static int ReadUTF8(FILE *infile, int count) { int c = 0; for(; count > 0; count--) c = (c << 6) | (fgetc(infile) & 0x3f); return c; } /* Get width of character, returns 1 for half width and 2 for full width */ static int GetCharWidth(int c) { static const int ranges[11][2] = { {0x1100, 0x115f}, {0x2329, 0x232a}, {0x2e80, 0x303e}, {0x3040, 0xa4cf}, {0xac00, 0xd7a3}, {0xf900, 0xfaff}, {0xfe10, 0xfe19}, {0xfe30, 0xfe6f}, {0xff00, 0xff60}, {0xffe0, 0xffe6}, {0x20000, 0x3fffd} }; int i; for(i = 0; i < 11; i++) { if( c >= ranges[i][0] && c <= ranges[i][1] ) return 2; } return 1; } /* Update cursor based on escape sequence */ static void UpdateCursor(FILE *infile, int *x, int *y) { int param = 0; int c; while( (c = fgetc(infile)) != EOF ) { if( c >= '0' && c <= '9' ) { param = param * 10 + c - '0'; } else if( c == ';' ) { /* Delimiter used for multi-param sequences (such as colors), but we don't care about those. */ } else if( c == 'A' ) { /* Cursor up */ *y -= param; return; } else if( c == 'B' ) { /* Cursor down */ *y += param; return; } else if( c == 'C' ) { /* Cursor forward */ *x += param; return; } else if( c == 'D' ) { /* Cursor backward */ *x -= param; if( *x < 0 ) *x = 0; return; } else { /* Some escape sequence that we don't care about, silently ignored */ return; } } } /* Load input characters to global buffer */ static void LoadInput(FILE *infile) { int c, x = 0, y = 0; int state = 0; /* 0 = normal, 1 = seen ESC */ while( (c = fgetc(infile)) != EOF ) { if( state != 0 ) { if( c == '[' ) { /* Escape sequence */ UpdateCursor(infile, &x, &y); continue; } state = 0; } if( c == 0x1b ) { /* Start of escape sequence */ state = 1; continue; } else if( (c & 0xe0) == 0xc0 ) { /* 2-byte UTF-8 sequence */ c = ((c & 0x1f) << 6) | ReadUTF8(infile, 1); AddChar(x, y, c); x += GetCharWidth(c); } else if( (c & 0xf0) == 0xe0 ) { /* 3-byte UTF-8 sequence */ c = ((c & 0x0f) << 12) | ReadUTF8(infile, 2); AddChar(x, y, c); x += GetCharWidth(c); } else if( (c & 0xf8) == 0xf0 ) { /* 4-byte UTF-8 sequence */ c = ((c & 0x07) << 18) | ReadUTF8(infile, 3); AddChar(x, y, c); x += GetCharWidth(c); } else if( c == '\n' ) { /* Newline: update cursor but don't buffer character */ y++; x = 0; } else if( c == ' ' ) { /* Space: update cursor but don't buffer character */ x++; } else if( c == '\t' ) { /* Tab: update cursor but don't buffer character */ for(x++; x % 8; x++); } else if( c < 32 ) { /* Other control characters (ignored) */ } else { /* Single byte character */ AddChar(x++, y, c); } } } int main(int argc, char **argv) { LoadInput(stdin); return 0; }