archive- Random tools & helpful resources for IRC |
git clone git://git.acid.vegas/archive.git |
Log | Files | Refs | Archive |
a2m.c (11826B)
1 /* Copyright (c) 2018 Trollforge. All rights reserved. 2 * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions 5 * are met: 6 * 1. Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * 2. Redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution. 11 * 3. Trollforge's name may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 */ 14 15 #include <ctype.h> 16 #include <errno.h> 17 #include <iconv.h> 18 #include <stdbool.h> 19 #include <stdint.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <sysexits.h> 24 #include <unistd.h> 25 #include <getopt.h> 26 27 #ifndef DEBUG 28 #define DEBUG 0 29 #else 30 #define DEBUG 1 31 #endif /* DEBUG */ 32 33 #define DPRINTF(fmt, ...) if (DEBUG) do { \ 34 fprintf(stderr, fmt, ##__VA_ARGS__); \ 35 } while (0) 36 37 #define DEFAULT_FG 7 38 #define DEFAULT_BG 0 39 #define DEFAULT_BOLD false 40 #define DEFAULT_ICE false 41 #define MAX_PARAMS 16 42 43 /* 4 bytes + '\0' */ 44 #define MAX_UTFSTR 5 45 46 typedef struct cell_s { 47 char *utfchar; 48 int fg; 49 int bg; 50 bool bold; 51 bool ice; 52 } cell_t; 53 54 typedef struct canvas_s { 55 cell_t **cell; /* canvas state */ 56 char fg; 57 char bg; 58 bool bold; 59 bool ice; 60 int row; /* cursor state */ 61 int col; 62 int bottomrow; 63 int oldrow; 64 int oldcol; 65 int max_cols; /* options */ 66 bool use_color; 67 bool show_unp; 68 int tab_size; 69 int lcrop; 70 int rcrop; 71 int default_fg; 72 int default_bg; 73 } canvas_t; 74 75 int get_fgcolor(cell_t *cell); 76 int get_bgcolor(cell_t *cell); 77 void print_color(canvas_t *c, cell_t *cell, int col); 78 void inc_row(canvas_t *c); 79 void draw_cell(canvas_t *c, char *inch, int fg, int bg, bool bold, bool ice); 80 void usage(void); 81 82 /* lol globals */ 83 84 /* 85 * ANSI COLORS BLK RED GRN YEL BLU MAG CYN WHT 86 * 0 1 2 3 4 5 6 7 87 */ 88 const char color[8] = { 1, 5, 3, 7, 2, 6, 10, 15 }; 89 const char color_bold[8] = { 14, 4, 9, 8, 12, 13, 11, 0 }; 90 91 int main(int argc, char *argv[]) { 92 int opt; 93 FILE *fd; 94 95 canvas_t *c; 96 97 c = (canvas_t *)calloc(1, sizeof(canvas_t)); 98 99 c->fg = DEFAULT_FG; 100 c->bg = DEFAULT_BG; 101 c->bold = false; 102 c->ice = false; 103 104 c->row = -1; 105 c->col = 0; 106 c->bottomrow = -1; 107 c->oldrow = 0; 108 c->oldcol = 0; 109 110 c->max_cols = 80; 111 c->use_color = true; 112 c->show_unp = false; 113 c->tab_size = 8; 114 c->lcrop = 0; 115 c->rcrop = 0; 116 c->default_fg = 7; 117 c->default_bg = 0; 118 119 while((opt = getopt(argc, argv, "npl:r:w:t:")) != -1) { 120 switch(opt) { 121 case 'n': 122 c->use_color = false; 123 break; 124 case 'p': 125 c->show_unp = true; 126 break; 127 case 'l': 128 c->lcrop = atoi(optarg); 129 break; 130 case 'r': 131 c->rcrop = atoi(optarg); 132 break; 133 case 'w': 134 c->max_cols = atoi(optarg); 135 break; 136 case 't': 137 c->tab_size = atoi(optarg); 138 break; 139 case '?': 140 /* fallthrough */ 141 case 'h': 142 /* fallthrough */ 143 default: 144 usage(); 145 } 146 } 147 argc -= optind; 148 argv += optind; 149 150 /* init the first line */ 151 inc_row(c); 152 153 fd = NULL; 154 155 if (argc == 1) 156 fd = fopen(argv[0], "r"); 157 else if (argc == 0) 158 fd = stdin; 159 else { 160 usage(); 161 } 162 163 if (!fd) { 164 perror(NULL); 165 exit(EX_NOINPUT); 166 } 167 168 char *charp = malloc(1); 169 170 if (!charp) { 171 perror(NULL); 172 exit(EX_OSERR); 173 } 174 175 char *space = " "; 176 177 int rc = 0; 178 int pcnt = 0; 179 int param[MAX_PARAMS]; 180 uint32_t ch = 0; 181 182 while ((ch = fgetc(fd)) != EOF) { 183 184 /* ignore sauce record */ 185 if (ch == 0x1a) 186 break; 187 188 /* drawable character */ 189 if (ch != 0x1b) { 190 *charp = (char)ch; 191 192 /* convert tabs to spaces */ 193 if (ch == '\t') 194 for (int i = 0; i < c->tab_size; i++) 195 draw_cell(c, space, c->fg, c->bg, c->bold, c->ice); 196 else if (ch == '\0') 197 draw_cell(c, space, DEFAULT_FG, DEFAULT_BG, c->bold, c->ice); 198 else if (ch != '\r' && ch != '\n') 199 draw_cell(c, charp, c->fg, c->bg, c->bold, c->ice); 200 201 if (ch == '\n') 202 while (c->col != 0) 203 draw_cell(c, space, c->fg, c->bg, c->bold, c->ice); 204 205 /* escape code */ 206 } else { 207 rc = 0; 208 pcnt = 0; 209 210 /* no rational way to recover tbqh imho */ 211 if ((ch = fgetc(fd)) != '[') { 212 DPRINTF("Invalid escape code, aborting at line %d\n", c->row + 1); 213 exit(EX_DATAERR); 214 } 215 216 while ((ch = fgetc(fd)) != EOF) { 217 218 /* parameters */ 219 if (isdigit(ch)) { 220 *charp = (char)ch; 221 rc = (rc * 10) + atoi(charp); 222 223 /* oddball parameter prefixes */ 224 } else if (ch == '?' || ch == '=' || ch == '>') { 225 /* just eat them for now */ 226 227 /* end of parameter, havent encountered ':' but its legit */ 228 } else if (ch == ';' || ch == ':') { 229 param[pcnt] = rc; 230 rc = 0; 231 pcnt++; 232 233 /* spacing */ 234 } else if (ch == 'C') { 235 /* default to one space */ 236 if (rc == 0) 237 rc = 1; 238 for (int i = 0; i < rc; i++) { 239 draw_cell(c, space, DEFAULT_FG, DEFAULT_BG, 240 DEFAULT_BOLD, DEFAULT_ICE); 241 242 } 243 rc = 0; 244 pcnt = 0; 245 break; 246 247 /* SGR sequence */ 248 } else if (ch == 'm') { 249 param[pcnt] = rc; 250 pcnt++; 251 for (int i = 0; i < pcnt; i++) { 252 /* reset */ 253 if (param[i] == 0) { 254 c->bold = false; 255 c->ice = false; 256 c->fg = DEFAULT_FG; 257 c->bg = DEFAULT_BG; 258 } else if (param[i] == 1) 259 c->bold = true; 260 else if (param[i] == 5) 261 c->ice = true; 262 else if (param[i] == 21 || param[i] == 22) 263 c->bold = false; 264 else if (param[i] == 25) 265 c->ice = false; 266 else if (param[i] >= 30 && param[i] <= 37) 267 c->fg = param[i] - 30; 268 else if (param[i] >= 90 && param[i] <= 97) { 269 c->fg = param[i] - 90; 270 c->bold = true; 271 } 272 else if (param[i] >= 40 && param[i] <= 47) 273 c->bg = param[i] - 40; 274 else if (param[i] == 39) 275 c->fg = DEFAULT_FG; 276 else if (param[i] == 49) 277 c->bg = DEFAULT_BG; 278 else 279 DPRINTF("Ignored SGR parameter %d at line %d\n", param[i], c->row + 1); 280 } 281 rc = 0; 282 pcnt = 0; 283 break; 284 285 /* clear screen */ 286 /* todo flesh this out */ 287 } else if (ch == 'J') { 288 c->row = 0; 289 c->col = 0; 290 291 for (int i = 0; i <= c->bottomrow; i++) { 292 for (int j = 0; j < c->max_cols; j++) { 293 c->cell[i][j].fg = DEFAULT_FG; 294 c->cell[i][j].bg = DEFAULT_BG; 295 c->cell[i][j].bold = DEFAULT_BOLD; 296 c->cell[i][j].ice = DEFAULT_ICE; 297 298 if (c->cell[i][j].utfchar) 299 free(c->cell[i][j].utfchar); 300 } 301 } 302 rc = 0; 303 pcnt = 0; 304 break; 305 306 /* move up */ 307 } else if (ch == 'A') { 308 309 /* default is 1 */ 310 if (rc == 0) 311 rc = 1; 312 313 while (rc > 0) { 314 315 /* found an ansi that tried this */ 316 /* probably from a bbs prelogin that assumed */ 317 /* frontdoor was eating the first few lines */ 318 if (c->row == 0) 319 break; 320 321 c->row--; 322 rc--; 323 } 324 325 rc = 0; 326 pcnt = 0; 327 break; 328 329 /* move down */ 330 } else if (ch == 'B') { 331 332 if (rc == 0) 333 rc = 1; 334 335 while (rc > 0) { 336 inc_row(c); 337 rc--; 338 } 339 340 rc = 0; 341 pcnt = 0; 342 break; 343 344 /* move forward */ 345 } else if (ch == 'C') { 346 347 if (rc == 0) 348 rc = 1; 349 350 while (rc > 0) { 351 352 if (c->col == c->max_cols - 1) 353 break; 354 355 c->col++; 356 rc--; 357 } 358 359 rc = 0; 360 pcnt = 0; 361 break; 362 363 /* move back */ 364 } else if (ch == 'D') { 365 366 if (rc == 0) 367 rc = 1; 368 369 while (rc > 0) { 370 if (c->col == 0) 371 break; 372 373 c->col--; 374 rc--; 375 } 376 377 rc = 0; 378 pcnt = 0; 379 break; 380 381 /* goto */ 382 /* todo add check for inc_row */ 383 } else if (ch == 'f' || ch == 'H') { 384 385 if (pcnt != 2) { 386 break; 387 } 388 389 c->row = param[0]; 390 c->col = param[1]; 391 392 rc = 0; 393 pcnt = 0; 394 break; 395 396 /* set mode */ 397 } else if (ch == 'h' || ch == 'n') { 398 399 rc = 0; 400 pcnt = 0; 401 break; 402 403 /* save position */ 404 } else if (ch == 's') { 405 406 c->oldrow = c->row; 407 c->oldcol = c->col; 408 409 rc = 0; 410 pcnt = 0; 411 break; 412 413 /* restore position */ 414 } else if (ch == 'u') { 415 416 c->row = c->oldrow; 417 c->col = c->oldcol; 418 419 rc = 0; 420 pcnt = 0; 421 break; 422 423 /* sequences we dont care and/or know about */ 424 } else { 425 DPRINTF("Ignored escape code ["); 426 427 for (int i = 0; i < pcnt; i++) 428 DPRINTF("%d%s", param[pcnt], i + 1 < pcnt ? ";" : ""); 429 430 DPRINTF("0x%02x at line %d col %d\n", ch, c->row + 1, c->col + 1); 431 432 rc = 0; 433 pcnt = 0; 434 break; 435 } 436 } 437 } 438 } 439 440 for (int i = 0; i <= c->row; i++) { 441 for (int j = c->lcrop; j < c->max_cols - c->rcrop; j++) { 442 cell_t *cell = &c->cell[i][j]; 443 444 if (!cell->utfchar) { 445 printf("\n"); 446 return (0); 447 } 448 449 if (c->use_color) 450 print_color(c, cell, j); 451 452 printf("%s", cell->utfchar); 453 } 454 printf("\n"); 455 } 456 457 if (argc == 1) 458 fclose(fd); 459 460 return (0); 461 } 462 463 void 464 usage(void) 465 { 466 fprintf(stderr, "usage: a2m [options] [input.ans]\n"); 467 fprintf(stderr, "\n"); 468 fprintf(stderr, " -l n Crop n lines from the left side.\n"); 469 fprintf(stderr, " -r n Crop n lines from the right side.\n"); 470 fprintf(stderr, " -n Disable color output.\n"); 471 fprintf(stderr, " -p Print unprintable characters.\n"); 472 fprintf(stderr, " -t size Specify tab size, default is 8.\n"); 473 fprintf(stderr, " -w width Specify width, default is 80.\n"); 474 exit(EX_USAGE); 475 } 476 477 int 478 get_fgcolor(cell_t *cell) 479 { 480 return (cell->bold ? color_bold[cell->fg] : color[cell->fg]); 481 } 482 483 int 484 get_bgcolor(cell_t *cell) 485 { 486 return (cell->ice ? color_bold[cell->bg] : color[cell->bg]); 487 } 488 489 void 490 print_color(canvas_t *c, cell_t *cell, int col) 491 { 492 static cell_t *prev = NULL; 493 494 int oldfg = (!prev || col == c->lcrop) ? DEFAULT_FG : prev->fg; 495 int oldbg = (!prev || col == c->lcrop) ? DEFAULT_BG : prev->bg; 496 int oldbold = (!prev || col == c->lcrop) ? DEFAULT_BOLD : prev->bold; 497 int oldice = (!prev || col == c->lcrop) ? DEFAULT_ICE : prev->ice; 498 499 if (cell->fg != oldfg || cell->bg != oldbg || cell->bold != oldbold || cell->ice != oldice) { 500 501 printf("\x03"); 502 503 printf("%d", get_fgcolor(cell)); 504 505 if (cell->bg != oldbg || (cell->ice != oldice)) 506 printf(",%d", get_bgcolor(cell)); 507 } 508 509 prev = cell; 510 return; 511 } 512 513 void 514 inc_row(canvas_t *c) 515 { 516 cell_t **newrows; 517 518 c->row++; 519 520 if (c->row <= c->bottomrow) 521 return; 522 523 c->bottomrow = c->row; 524 525 newrows = (cell_t **)calloc(c->row + 1, sizeof(cell_t *)); 526 527 if (!newrows) { 528 perror(NULL); 529 exit(EX_OSERR); 530 } 531 532 for (int i = 0; i < c->row; i++) 533 newrows[i] = c->cell[i]; 534 535 newrows[c->row] = (cell_t *)calloc(c->max_cols, sizeof(cell_t)); 536 537 free(c->cell); 538 c->cell = newrows; 539 540 for (int j = 0; j < c->max_cols; j++) { 541 c->cell[c->row][j].fg = DEFAULT_FG; 542 c->cell[c->row][j].bg = DEFAULT_BG; 543 c->cell[c->row][j].bold = DEFAULT_BOLD; 544 c->cell[c->row][j].ice = DEFAULT_ICE; 545 } 546 return; 547 } 548 549 void 550 draw_cell(canvas_t *c, char *inch, int fg, int bg, bool bold, bool ice) 551 { 552 static iconv_t conv = (iconv_t)0; 553 char *outchp; 554 char *oldoutchp; 555 556 outchp = calloc(1, MAX_UTFSTR); 557 558 if (!outchp) { 559 perror(NULL); 560 exit(EX_OSERR); 561 } 562 563 oldoutchp = outchp; 564 565 if ((unsigned char)inch[0] < 32 && !c->show_unp) 566 inch[0] = ' '; 567 568 size_t inchsize = 1; 569 size_t outchsize = MAX_UTFSTR; 570 571 if (!conv) 572 conv = iconv_open("UTF-8", "CP437"); 573 574 iconv(conv, &inch, &inchsize, &outchp, &outchsize); 575 576 c->cell[c->row][c->col].utfchar = calloc(1, MAX_UTFSTR); 577 if (!c->cell[c->row][c->col].utfchar) { 578 perror(NULL); 579 exit(EX_OSERR); 580 } 581 582 strcpy(c->cell[c->row][c->col].utfchar, oldoutchp); 583 584 free(oldoutchp); 585 586 c->cell[c->row][c->col].fg = fg; 587 c->cell[c->row][c->col].bg = bg; 588 c->cell[c->row][c->col].bold = bold; 589 c->cell[c->row][c->col].ice = ice; 590 591 c->col++; 592 593 if (c->col == c->max_cols) { 594 inc_row(c); 595 c->col = 0; 596 } 597 return; 598 }