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 }