a2m/a2m.c

599 lines
12 KiB
C
Raw Normal View History

2018-07-14 14:47:17 -07:00
/* Copyright (c) 2018 Trollforge. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Trollforge's name may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*/
2016-09-06 17:01:56 -07:00
#include <ctype.h>
#include <errno.h>
#include <iconv.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2016-09-21 12:48:55 -07:00
#include <sysexits.h>
2016-09-06 17:01:56 -07:00
#include <unistd.h>
2016-09-10 04:00:26 -07:00
#include <getopt.h>
2016-09-06 17:01:56 -07:00
2016-09-21 14:18:50 -07:00
#ifndef DEBUG
#define DEBUG 0
#else
#define DEBUG 1
#endif /* DEBUG */
2016-09-21 12:48:55 -07:00
#define DPRINTF(fmt, ...) if (DEBUG) do { \
2016-09-21 14:18:50 -07:00
fprintf(stderr, fmt, ##__VA_ARGS__); \
2016-09-21 12:48:55 -07:00
} while (0)
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
#define DEFAULT_FG 7
#define DEFAULT_BG 0
#define DEFAULT_BOLD false
#define DEFAULT_ICE false
#define MAX_PARAMS 16
2016-09-06 17:01:56 -07:00
/* 4 bytes + '\0' */
#define MAX_UTFSTR 5
typedef struct cell_s {
2016-09-21 12:48:55 -07:00
char *utfchar;
int fg;
int bg;
bool bold;
bool ice;
2016-09-06 17:01:56 -07:00
} cell_t;
2016-09-21 10:40:19 -07:00
typedef struct canvas_s {
2016-09-21 12:48:55 -07:00
cell_t **cell; /* canvas state */
char fg;
char bg;
bool bold;
bool ice;
int row; /* cursor state */
int col;
int bottomrow;
int oldrow;
int oldcol;
int max_cols; /* options */
bool use_color;
bool show_unp;
int tab_size;
int lcrop;
int rcrop;
int default_fg;
int default_bg;
2016-09-21 10:40:19 -07:00
} canvas_t;
2016-09-21 12:48:55 -07:00
int get_fgcolor(cell_t *cell);
int get_bgcolor(cell_t *cell);
void print_color(canvas_t *c, cell_t *cell, int col);
void inc_row(canvas_t *c);
void draw_cell(canvas_t *c, char *inch, int fg, int bg, bool bold, bool ice);
void usage(void);
2016-09-06 17:01:56 -07:00
/* lol globals */
2016-09-21 12:48:55 -07:00
/*
* ANSI COLORS BLK RED GRN YEL BLU MAG CYN WHT
* 0 1 2 3 4 5 6 7
*/
2016-09-06 17:01:56 -07:00
const char color[8] = { 1, 5, 3, 7, 2, 6, 10, 15 };
const char color_bold[8] = { 14, 4, 9, 8, 12, 13, 11, 0 };
int main(int argc, char *argv[]) {
int opt;
2016-09-21 12:48:55 -07:00
FILE *fd;
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
canvas_t *c;
c = (canvas_t *)calloc(1, sizeof(canvas_t));
2016-09-21 10:40:19 -07:00
c->fg = DEFAULT_FG;
c->bg = DEFAULT_BG;
c->bold = false;
c->ice = false;
c->row = -1;
c->col = 0;
c->bottomrow = -1;
c->oldrow = 0;
c->oldcol = 0;
c->max_cols = 80;
c->use_color = true;
c->show_unp = false;
c->tab_size = 8;
c->lcrop = 0;
c->rcrop = 0;
c->default_fg = 7;
c->default_bg = 0;
2016-09-06 17:01:56 -07:00
while((opt = getopt(argc, argv, "npl:r:w:t:")) != -1) {
switch(opt) {
2016-09-21 12:48:55 -07:00
case 'n':
c->use_color = false;
break;
case 'p':
c->show_unp = true;
break;
case 'l':
c->lcrop = atoi(optarg);
break;
case 'r':
c->rcrop = atoi(optarg);
break;
case 'w':
c->max_cols = atoi(optarg);
break;
case 't':
c->tab_size = atoi(optarg);
break;
case '?':
/* fallthrough */
case 'h':
/* fallthrough */
default:
usage();
2016-09-06 17:01:56 -07:00
}
}
argc -= optind;
argv += optind;
2016-09-06 17:16:45 -07:00
/* init the first line */
2016-09-21 10:40:19 -07:00
inc_row(c);
2016-09-21 12:48:55 -07:00
fd = NULL;
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
if (argc == 1)
fd = fopen(argv[0], "r");
else if (argc == 0)
fd = stdin;
else {
2016-09-06 17:01:56 -07:00
usage();
}
2016-09-21 12:48:55 -07:00
if (!fd) {
2016-09-06 17:01:56 -07:00
perror(NULL);
2016-09-21 12:48:55 -07:00
exit(EX_NOINPUT);
2016-09-06 17:01:56 -07:00
}
char *charp = malloc(1);
2016-09-21 12:48:55 -07:00
2016-09-06 17:01:56 -07:00
if (!charp) {
perror(NULL);
2016-09-21 12:48:55 -07:00
exit(EX_OSERR);
2016-09-06 17:01:56 -07:00
}
char *space = " ";
int rc = 0;
int pcnt = 0;
int param[MAX_PARAMS];
uint32_t ch = 0;
2016-09-21 12:48:55 -07:00
while ((ch = fgetc(fd)) != EOF) {
2016-09-06 17:01:56 -07:00
/* ignore sauce record */
2016-09-21 12:48:55 -07:00
if (ch == 0x1a)
2016-09-06 17:01:56 -07:00
break;
/* drawable character */
2016-09-21 12:48:55 -07:00
if (ch != 0x1b) {
2016-09-06 17:01:56 -07:00
*charp = (char)ch;
/* convert tabs to spaces */
2016-09-21 12:48:55 -07:00
if (ch == '\t')
for (int i = 0; i < c->tab_size; i++)
2016-09-21 10:40:19 -07:00
draw_cell(c, space, c->fg, c->bg, c->bold, c->ice);
2016-09-21 12:48:55 -07:00
else if (ch == '\0')
2016-09-21 10:40:19 -07:00
draw_cell(c, space, DEFAULT_FG, DEFAULT_BG, c->bold, c->ice);
2016-09-21 12:48:55 -07:00
else if (ch != '\r' && ch != '\n')
2016-09-21 10:40:19 -07:00
draw_cell(c, charp, c->fg, c->bg, c->bold, c->ice);
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
if (ch == '\n')
while (c->col != 0)
2016-09-21 10:40:19 -07:00
draw_cell(c, space, c->fg, c->bg, c->bold, c->ice);
2016-09-06 17:01:56 -07:00
/* escape code */
} else {
rc = 0;
pcnt = 0;
2016-09-21 10:40:19 -07:00
2016-09-06 17:01:56 -07:00
/* no rational way to recover tbqh imho */
2016-09-21 12:48:55 -07:00
if ((ch = fgetc(fd)) != '[') {
2016-09-21 14:18:50 -07:00
DPRINTF("Invalid escape code, aborting at line %d\n", c->row + 1);
2016-09-21 12:48:55 -07:00
exit(EX_DATAERR);
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
while ((ch = fgetc(fd)) != EOF) {
2016-09-21 10:40:19 -07:00
2016-09-06 17:01:56 -07:00
/* parameters */
if (isdigit(ch)) {
*charp = (char)ch;
rc = (rc * 10) + atoi(charp);
/* oddball parameter prefixes */
} else if (ch == '?' || ch == '=' || ch == '>') {
2016-09-21 12:48:55 -07:00
/* just eat them for now */
2016-09-06 17:01:56 -07:00
/* end of parameter, havent encountered ':' but its legit */
} else if (ch == ';' || ch == ':') {
param[pcnt] = rc;
rc = 0;
pcnt++;
/* spacing */
} else if (ch == 'C') {
/* default to one space */
2016-09-21 12:48:55 -07:00
if (rc == 0)
2016-09-06 17:01:56 -07:00
rc = 1;
for (int i = 0; i < rc; i++) {
2016-09-21 10:40:19 -07:00
draw_cell(c, space, DEFAULT_FG, DEFAULT_BG,
2016-09-06 17:01:56 -07:00
DEFAULT_BOLD, DEFAULT_ICE);
}
rc = 0;
pcnt = 0;
break;
/* SGR sequence */
} else if (ch == 'm') {
param[pcnt] = rc;
pcnt++;
for (int i = 0; i < pcnt; i++) {
/* reset */
if (param[i] == 0) {
2016-09-21 10:40:19 -07:00
c->bold = false;
c->ice = false;
c->fg = DEFAULT_FG;
c->bg = DEFAULT_BG;
2016-09-21 12:48:55 -07:00
} else if (param[i] == 1)
2016-09-21 10:40:19 -07:00
c->bold = true;
2016-09-21 12:48:55 -07:00
else if (param[i] == 5)
2016-09-21 10:40:19 -07:00
c->ice = true;
2016-09-21 12:48:55 -07:00
else if (param[i] == 21 || param[i] == 22)
2016-09-21 10:40:19 -07:00
c->bold = false;
2016-09-21 12:48:55 -07:00
else if (param[i] == 25)
2016-09-21 10:40:19 -07:00
c->ice = false;
2016-09-21 12:48:55 -07:00
else if (param[i] >= 30 && param[i] <= 37)
2016-09-21 10:40:19 -07:00
c->fg = param[i] - 30;
2018-06-23 21:02:39 -07:00
else if (param[i] >= 90 && param[i] <= 97) {
c->fg = param[i] - 90;
c->bold = true;
}
2016-09-21 12:48:55 -07:00
else if (param[i] >= 40 && param[i] <= 47)
2016-09-21 10:40:19 -07:00
c->bg = param[i] - 40;
2016-09-21 12:48:55 -07:00
else if (param[i] == 39)
2016-09-21 10:40:19 -07:00
c->fg = DEFAULT_FG;
2016-09-21 12:48:55 -07:00
else if (param[i] == 49)
2016-09-21 10:40:19 -07:00
c->bg = DEFAULT_BG;
2016-09-21 12:48:55 -07:00
else
2016-09-21 14:18:50 -07:00
DPRINTF("Ignored SGR parameter %d at line %d\n", param[i], c->row + 1);
2016-09-06 17:01:56 -07:00
}
rc = 0;
pcnt = 0;
break;
/* clear screen */
/* todo flesh this out */
2016-09-21 10:40:19 -07:00
} else if (ch == 'J') {
c->row = 0;
c->col = 0;
for (int i = 0; i <= c->bottomrow; i++) {
for (int j = 0; j < c->max_cols; j++) {
c->cell[i][j].fg = DEFAULT_FG;
c->cell[i][j].bg = DEFAULT_BG;
c->cell[i][j].bold = DEFAULT_BOLD;
c->cell[i][j].ice = DEFAULT_ICE;
2016-09-21 12:48:55 -07:00
if (c->cell[i][j].utfchar)
2016-09-21 10:40:19 -07:00
free(c->cell[i][j].utfchar);
2016-09-06 17:01:56 -07:00
}
}
rc = 0;
pcnt = 0;
break;
/* move up */
} else if (ch == 'A') {
/* default is 1 */
2016-09-21 12:48:55 -07:00
if (rc == 0)
2016-09-06 17:01:56 -07:00
rc = 1;
while (rc > 0) {
/* found an ansi that tried this */
/* probably from a bbs prelogin that assumed */
/* frontdoor was eating the first few lines */
2016-09-21 12:48:55 -07:00
if (c->row == 0)
2016-09-06 17:01:56 -07:00
break;
2016-09-21 10:40:19 -07:00
c->row--;
2016-09-06 17:01:56 -07:00
rc--;
}
rc = 0;
pcnt = 0;
break;
/* move down */
} else if (ch == 'B') {
2016-09-21 12:48:55 -07:00
if (rc == 0)
2016-09-06 17:01:56 -07:00
rc = 1;
while (rc > 0) {
2016-09-21 10:40:19 -07:00
inc_row(c);
2016-09-06 17:01:56 -07:00
rc--;
}
rc = 0;
pcnt = 0;
break;
/* move forward */
} else if (ch == 'C') {
2016-09-21 12:48:55 -07:00
if (rc == 0)
2016-09-06 17:01:56 -07:00
rc = 1;
while (rc > 0) {
2016-09-21 12:48:55 -07:00
if (c->col == c->max_cols - 1)
2016-09-06 17:01:56 -07:00
break;
2016-09-21 10:40:19 -07:00
c->col++;
2016-09-06 17:01:56 -07:00
rc--;
}
rc = 0;
pcnt = 0;
break;
/* move back */
} else if (ch == 'D') {
2016-09-21 12:48:55 -07:00
if (rc == 0)
2016-09-06 17:01:56 -07:00
rc = 1;
while (rc > 0) {
2016-09-21 12:48:55 -07:00
if (c->col == 0)
2016-09-06 17:01:56 -07:00
break;
2016-09-21 10:40:19 -07:00
c->col--;
2016-09-06 17:01:56 -07:00
rc--;
}
rc = 0;
pcnt = 0;
break;
/* goto */
2016-09-21 10:40:19 -07:00
/* todo add check for inc_row */
2016-09-06 17:01:56 -07:00
} else if (ch == 'f' || ch == 'H') {
if (pcnt != 2) {
break;
}
2016-09-21 10:40:19 -07:00
c->row = param[0];
c->col = param[1];
2016-09-06 17:01:56 -07:00
rc = 0;
pcnt = 0;
break;
/* set mode */
} else if (ch == 'h' || ch == 'n') {
rc = 0;
pcnt = 0;
break;
/* save position */
} else if (ch == 's') {
2016-09-21 10:40:19 -07:00
c->oldrow = c->row;
c->oldcol = c->col;
2016-09-06 17:01:56 -07:00
rc = 0;
pcnt = 0;
break;
/* restore position */
} else if (ch == 'u') {
2016-09-21 10:40:19 -07:00
c->row = c->oldrow;
c->col = c->oldcol;
2016-09-06 17:01:56 -07:00
rc = 0;
pcnt = 0;
break;
/* sequences we dont care and/or know about */
} else {
2016-09-21 14:18:50 -07:00
DPRINTF("Ignored escape code [");
2016-09-21 12:48:55 -07:00
for (int i = 0; i < pcnt; i++)
2016-09-21 14:18:50 -07:00
DPRINTF("%d%s", param[pcnt], i + 1 < pcnt ? ";" : "");
2016-09-21 12:48:55 -07:00
2016-09-21 14:18:50 -07:00
DPRINTF("0x%02x at line %d col %d\n", ch, c->row + 1, c->col + 1);
2016-09-06 17:01:56 -07:00
rc = 0;
pcnt = 0;
break;
}
}
}
}
2016-09-21 10:40:19 -07:00
for (int i = 0; i <= c->row; i++) {
for (int j = c->lcrop; j < c->max_cols - c->rcrop; j++) {
cell_t *cell = &c->cell[i][j];
2016-09-06 17:01:56 -07:00
if (!cell->utfchar) {
printf("\n");
2016-09-21 12:48:55 -07:00
return (0);
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
if (c->use_color)
2016-09-21 10:40:19 -07:00
print_color(c, cell, j);
2016-09-06 17:01:56 -07:00
printf("%s", cell->utfchar);
}
printf("\n");
}
2016-09-21 12:48:55 -07:00
if (argc == 1)
fclose(fd);
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
return (0);
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
void
usage(void)
{
fprintf(stderr, "usage: a2m [options] [input.ans]\n");
2016-09-06 17:01:56 -07:00
fprintf(stderr, "\n");
fprintf(stderr, " -l n Crop n lines from the left side.\n");
fprintf(stderr, " -r n Crop n lines from the right side.\n");
fprintf(stderr, " -n Disable color output.\n");
fprintf(stderr, " -p Print unprintable characters.\n");
2016-09-21 10:40:19 -07:00
fprintf(stderr, " -t size Specify tab size, default is 8.\n");
fprintf(stderr, " -w width Specify width, default is 80.\n");
2016-09-21 12:48:55 -07:00
exit(EX_USAGE);
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
int
get_fgcolor(cell_t *cell)
{
return (cell->bold ? color_bold[cell->fg] : color[cell->fg]);
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
int
get_bgcolor(cell_t *cell)
{
return (cell->ice ? color_bold[cell->bg] : color[cell->bg]);
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
void
print_color(canvas_t *c, cell_t *cell, int col)
{
2016-09-06 17:01:56 -07:00
static cell_t *prev = NULL;
2016-09-21 20:17:46 -07:00
int oldfg = (!prev || col == c->lcrop) ? DEFAULT_FG : prev->fg;
int oldbg = (!prev || col == c->lcrop) ? DEFAULT_BG : prev->bg;
int oldbold = (!prev || col == c->lcrop) ? DEFAULT_BOLD : prev->bold;
int oldice = (!prev || col == c->lcrop) ? DEFAULT_ICE : prev->ice;
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
if (cell->fg != oldfg || cell->bg != oldbg || cell->bold != oldbold || cell->ice != oldice) {
2016-09-06 17:01:56 -07:00
printf("\x03");
2018-06-23 17:08:39 -07:00
printf("%d", get_fgcolor(cell));
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
if (cell->bg != oldbg || (cell->ice != oldice))
2016-09-06 17:01:56 -07:00
printf(",%d", get_bgcolor(cell));
}
prev = cell;
return;
}
2016-09-21 12:48:55 -07:00
void
inc_row(canvas_t *c)
{
cell_t **newrows;
2016-09-21 10:40:19 -07:00
c->row++;
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
if (c->row <= c->bottomrow)
2016-09-06 17:01:56 -07:00
return;
2016-09-21 10:40:19 -07:00
c->bottomrow = c->row;
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
newrows = (cell_t **)calloc(c->row + 1, sizeof(cell_t *));
2016-09-06 17:01:56 -07:00
if (!newrows) {
perror(NULL);
2016-09-21 12:48:55 -07:00
exit(EX_OSERR);
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
for (int i = 0; i < c->row; i++)
2016-09-21 10:40:19 -07:00
newrows[i] = c->cell[i];
2016-09-06 17:01:56 -07:00
2016-09-21 10:40:19 -07:00
newrows[c->row] = (cell_t *)calloc(c->max_cols, sizeof(cell_t));
2016-09-06 17:01:56 -07:00
2016-09-21 10:40:19 -07:00
free(c->cell);
c->cell = newrows;
2016-09-06 17:01:56 -07:00
2016-09-21 10:40:19 -07:00
for (int j = 0; j < c->max_cols; j++) {
c->cell[c->row][j].fg = DEFAULT_FG;
c->cell[c->row][j].bg = DEFAULT_BG;
c->cell[c->row][j].bold = DEFAULT_BOLD;
c->cell[c->row][j].ice = DEFAULT_ICE;
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
return;
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
void
draw_cell(canvas_t *c, char *inch, int fg, int bg, bool bold, bool ice)
{
2016-09-06 17:01:56 -07:00
static iconv_t conv = (iconv_t)0;
2016-09-21 12:48:55 -07:00
char *outchp;
char *oldoutchp;
outchp = calloc(1, MAX_UTFSTR);
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
if (!outchp) {
2016-09-06 17:01:56 -07:00
perror(NULL);
2016-09-21 12:48:55 -07:00
exit(EX_OSERR);
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
oldoutchp = outchp;
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
if ((unsigned char)inch[0] < 32 && !c->show_unp)
inch[0] = ' ';
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
size_t inchsize = 1;
size_t outchsize = MAX_UTFSTR;
if (!conv)
2016-09-06 17:01:56 -07:00
conv = iconv_open("UTF-8", "CP437");
2016-09-21 12:48:55 -07:00
iconv(conv, &inch, &inchsize, &outchp, &outchsize);
2016-09-06 17:01:56 -07:00
2016-09-21 10:40:19 -07:00
c->cell[c->row][c->col].utfchar = calloc(1, MAX_UTFSTR);
if (!c->cell[c->row][c->col].utfchar) {
2016-09-06 17:01:56 -07:00
perror(NULL);
2016-09-21 12:48:55 -07:00
exit(EX_OSERR);
2016-09-06 17:01:56 -07:00
}
2016-09-21 12:48:55 -07:00
strcpy(c->cell[c->row][c->col].utfchar, oldoutchp);
2016-09-06 17:01:56 -07:00
2016-09-21 12:48:55 -07:00
free(oldoutchp);
2016-09-06 17:01:56 -07:00
2016-09-21 10:40:19 -07:00
c->cell[c->row][c->col].fg = fg;
c->cell[c->row][c->col].bg = bg;
c->cell[c->row][c->col].bold = bold;
c->cell[c->row][c->col].ice = ice;
2016-09-06 17:01:56 -07:00
2016-09-21 10:40:19 -07:00
c->col++;
2016-09-06 17:01:56 -07:00
2016-09-21 10:40:19 -07:00
if (c->col == c->max_cols) {
inc_row(c);
c->col = 0;
2016-09-06 17:01:56 -07:00
}
return;
}