archive

- Random tools & helpful resources for IRC
git clone git://git.acid.vegas/archive.git
Log | Files | Refs | Archive

tdfiglet.c (12031B)

      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 <dirent.h>
     17 #include <errno.h>
     18 #include <fcntl.h>
     19 #include <getopt.h>
     20 #include <iconv.h>
     21 #include <stdbool.h>
     22 #include <stdint.h>
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <sysexits.h>
     27 #include <unistd.h>
     28 
     29 #include <sys/mman.h>
     30 #include <sys/queue.h>
     31 #include <sys/stat.h>
     32 #include <sys/time.h>
     33 #include <sys/types.h>
     34 
     35 #ifdef DEBUG
     36 #define DEBUG 1
     37 #endif /* DEBUG */
     38 
     39 #define OUTLN_FNT 0
     40 #define BLOCK_FNT 1
     41 #define COLOR_FNT 2
     42 
     43 #define NUM_CHARS 94
     44 
     45 /* 4 bytes + '\0' */
     46 #define MAX_UTFSTR	5
     47 
     48 #define LEFT_JUSTIFY	0
     49 #define RIGHT_JUSTIFY	1
     50 #define CENTER_JUSTIFY	2
     51 
     52 #define DEFAULT_WIDTH	80
     53 
     54 #define COLOR_ANSI	0
     55 #define COLOR_MIRC	1
     56 
     57 #define ENC_UNICODE	0
     58 #define ENC_ANSI	1
     59 
     60 #ifndef FONT_DIR
     61 #define FONT_DIR	"fonts"
     62 #endif /* FONT_DIR */
     63 
     64 #ifndef FONT_EXT
     65 #define FONT_EXT	"tdf"
     66 #endif /* FONT_EXT */
     67 
     68 #ifndef DEFAULT_FONT
     69 #define DEFAULT_FONT	"brndamgx" /* seems most complete */
     70 #endif /* DEFAULT_FONT */
     71 
     72 typedef struct opt_s {
     73 	uint8_t justify;
     74 	uint8_t width;
     75 	uint8_t color;
     76 	uint8_t encoding;
     77 	bool random;
     78 	bool info;
     79 } opt_t;
     80 
     81 typedef struct cell_s {
     82 	uint8_t color;
     83 	char utfchar[MAX_UTFSTR];
     84 } cell_t;
     85 
     86 typedef struct glyph_s {
     87 	uint8_t width;
     88 	uint8_t height;
     89 	cell_t *cell;
     90 } glyph_t;
     91 
     92 typedef struct font_s {
     93 	uint8_t namelen;
     94 	uint8_t *name;
     95 	uint8_t fonttype;
     96 	uint8_t spacing;
     97 	uint16_t blocksize;
     98 	uint16_t *charlist;
     99 	uint8_t *data;
    100 	glyph_t *glyphs[NUM_CHARS];
    101 	uint8_t height;
    102 } font_t;
    103 
    104 struct dirname_s {
    105 	char *str;
    106 	SLIST_ENTRY(dirname_s) stuff;
    107 };
    108 
    109 const char *charlist = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO"
    110 		       "PQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
    111 
    112 opt_t opt;
    113 
    114 void usage(void);
    115 font_t *loadfont(char *fn);
    116 void readchar(int i, glyph_t *glyph, font_t *font);
    117 
    118 void ibmtoutf8(char *a, char *u);
    119 void printcolor(uint8_t color);
    120 int lookupchar(char c, const font_t *font);
    121 
    122 void printcell(char *utfchar, uint8_t color);
    123 void printrow(const glyph_t *glyph, int row);
    124 void printstr(const char *str, font_t *font);
    125 
    126 void
    127 usage(void)
    128 {
    129 	fprintf(stderr, "usage: tdfiglet [options] input\n");
    130 	fprintf(stderr, "\n");
    131 	fprintf(stderr, "    -f [font] Specify font file used.\n");
    132 	fprintf(stderr, "    -j l|r|c  Justify left, right, or center.  Default is left.\n");
    133 	fprintf(stderr, "    -w n      Set screen width.  Default is 80.\n");
    134 	fprintf(stderr, "    -c a|m    Color format ANSI or mirc.  Default is ANSI.\n");
    135 	fprintf(stderr, "    -e u|a    Encode as unicode or ASCII.  Default is unicode.\n");
    136 	fprintf(stderr, "    -i        Print font details.\n");
    137 	fprintf(stderr, "    -r        Use random font.\n");
    138 	fprintf(stderr, "    -h        Print usage.\n");
    139 	fprintf(stderr, "\n");
    140 	exit(EX_USAGE);
    141 }
    142 
    143 int
    144 main(int argc, char *argv[])
    145 {
    146 	font_t *font = NULL;
    147 	int o;
    148 
    149 	opt.justify = LEFT_JUSTIFY;
    150 	opt.width = 80;
    151 	opt.info = false;
    152 	opt.encoding = ENC_UNICODE;
    153 	opt.random = false;
    154 	char *fontfile = NULL;
    155 
    156 	struct timeval tv;
    157 
    158 	DIR *d;
    159 	struct dirent *dir;
    160 	SLIST_HEAD(, dirname_s) head = SLIST_HEAD_INITIALIZER(dirname_s);
    161 	SLIST_INIT(&head);
    162 	struct dirname_s *dp;
    163 
    164 	int r = 0;
    165 	int dll = 0;
    166 
    167 	while((o = getopt(argc, argv, "f:w:j:c:e:ir")) != -1) {
    168 		switch (o) {
    169 			case 'f':
    170 				fontfile = optarg;
    171 				break;
    172 			case 'w':
    173 				opt.width = atoi(optarg);
    174 				break;
    175 			case 'j':
    176 				switch (optarg[0]) {
    177 					case 'l':
    178 						opt.justify = LEFT_JUSTIFY;
    179 						break;
    180 					case 'r':
    181 						opt.justify = RIGHT_JUSTIFY;
    182 						break;
    183 					case 'c':
    184 						opt.justify = CENTER_JUSTIFY;
    185 						break;
    186 					default:
    187 						usage();
    188 				}
    189 				break;
    190 			case 'c':
    191 				switch (optarg[0]) {
    192 					case 'a':
    193 						opt.color = COLOR_ANSI;
    194 						break;
    195 					case 'm':
    196 						opt.color = COLOR_MIRC;
    197 						break;
    198 					default:
    199 						usage();
    200 				}
    201 				break;
    202 			case 'e':
    203 				switch (optarg[0]) {
    204 					case 'a':
    205 						opt.encoding = ENC_ANSI;
    206 						break;
    207 					case 'u':
    208 						opt.encoding = ENC_UNICODE;
    209 						break;
    210 					default:
    211 						usage();
    212 				}
    213 				break;
    214 			case 'i':
    215 				opt.info = true;
    216 				break;
    217 			case 'r':
    218 				opt.random = true;
    219 				break;
    220 			case 'h':
    221 				/* fallthrough */
    222 			default:
    223 				usage();
    224 		}
    225 	}
    226 
    227 	argc -= optind;
    228 	argv += optind;
    229 
    230 	if (argc < 1) {
    231 		usage();
    232 	}
    233 
    234 	if (!fontfile) {
    235 		if (!opt.random) {
    236 			fontfile = DEFAULT_FONT;
    237 		} else {
    238 			d = opendir(FONT_DIR);
    239 			if (!d) {
    240 				fprintf(stderr, "Error: unable to read %s\n",
    241 					FONT_DIR);
    242 				exit(1);
    243 			}
    244 
    245 			while ((dir = readdir(d))) {
    246 				if (strstr(dir->d_name, FONT_EXT)) {
    247 					dp = malloc(sizeof(struct dirname_s));
    248 					dp->str = calloc(1, 1024);
    249 					strcpy(dp->str, dir->d_name);
    250 					SLIST_INSERT_HEAD(&head, dp, stuff);
    251 					dll++;
    252 				}
    253 			}
    254 			closedir(d);
    255 
    256 			gettimeofday(&tv, NULL);
    257 
    258 			srand(tv.tv_usec);
    259 			r = dll ? rand() % dll : 0;
    260 
    261 			dp = SLIST_FIRST(&head);
    262 			for (int i = 0; i < r; i++) {
    263 				dp = SLIST_NEXT(dp, stuff);
    264 			}
    265 
    266 			if (dp && dp->str) {
    267 				fontfile = dp->str;
    268 			} else {
    269 				fontfile = DEFAULT_FONT;
    270 			}
    271 		}
    272 	}
    273 
    274 	font = loadfont(fontfile);
    275 
    276 	printf("\n");
    277 
    278 	for (int i = 0; i < argc; i++) {
    279 		printstr(argv[i], font);
    280 		printf("\n");
    281 	}
    282 
    283 	return(0);
    284 }
    285 
    286 font_t
    287 *loadfont(char *fn_arg)
    288 {
    289 
    290 	font_t *font;
    291 	uint8_t *map = NULL;
    292 	int fd;
    293 	struct stat st;
    294 	size_t len;
    295 	uint8_t *p;
    296 	char *fn = NULL;
    297 
    298 	const char *magic = "\x13TheDraw FONTS file\x1a";
    299 
    300 	if (!strchr(fn_arg, '/')) {
    301 		if (strchr(fn_arg, '.')) {
    302 			fn = malloc(strlen(FONT_DIR) + strlen(fn_arg) + 2);
    303 			sprintf(fn, "%s/%s", FONT_DIR, fn_arg);
    304 		} else {
    305 			fn = malloc(strlen(FONT_DIR) + strlen(fn_arg) + \
    306 				strlen(FONT_EXT) + 3);
    307 			sprintf(fn, "%s/%s.%s", FONT_DIR, fn_arg, FONT_EXT);
    308 		}
    309 	} else {
    310 		fn = malloc(strlen(fn_arg) + 1);
    311 		sprintf(fn, "%s", fn_arg);
    312 	}
    313 
    314 	if (fn == NULL) {
    315 		perror(NULL);
    316 		exit(EX_OSERR);
    317 	}
    318 
    319 	fd = open(fn, O_RDONLY);
    320 
    321 	if (fd < 0) {
    322 		perror(NULL);
    323 		exit(EX_NOINPUT);
    324 	}
    325 
    326 	if (opt.info) {
    327 		printf("file: %s\n", fn);
    328 	}
    329 
    330 	font = malloc(sizeof(font_t));
    331 
    332 	if (fstat(fd, &st)) {
    333 		perror(NULL);
    334 		exit(EX_OSERR);
    335 	}
    336 
    337 	len = st.st_size;
    338 
    339 	map = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
    340 
    341 	if (!map) {
    342 		perror(NULL);
    343 		exit(EX_OSERR);
    344 	}
    345 
    346 	close(fd);
    347 
    348 	if (!font) {
    349 		perror(NULL);
    350 		exit(EX_OSERR);
    351 	}
    352 
    353 	font->namelen = map[24];
    354 	font->name = &map[25];
    355 	font->fonttype = map[41];
    356 	font->spacing = map[42];
    357 	font->blocksize = (uint16_t)map[43];
    358 	font->charlist = (uint16_t *)&map[45];
    359 	font->data = &map[233];
    360 	font->height = 0;
    361 
    362 	if (strncmp(magic, (const char *)map, strlen(magic)) || font->fonttype != COLOR_FNT) {
    363 		fprintf(stderr, "Invalid font file: %s\n", fn);
    364 		exit(EX_NOINPUT);
    365 	}
    366 
    367 	free(fn);
    368 
    369 	if (opt.info) {
    370 		printf("font: %s\nchar list: ", font->name);
    371 	}
    372 
    373 	for (int i = 0; i < NUM_CHARS; i++) {
    374 		/* check for invalid glyph addresses */
    375 		if (charlist[i] + &map[233] > map + st.st_size) {
    376 			perror(NULL);
    377 			exit(EX_NOINPUT);
    378 		}
    379 
    380 		if (lookupchar(charlist[i], font) > -1) {
    381 
    382 			if (opt.info) {
    383 				printf("%c", charlist[i]);
    384 			}
    385 
    386 			p = font->data + font->charlist[i] + 2;
    387 			if (*p > font->height) {
    388 				font->height = *p;
    389 			}
    390 		}
    391 	}
    392 
    393 	if (opt.info) {
    394 		printf("\n");
    395 	}
    396 
    397 	for (int i = 0; i < NUM_CHARS; i++) {
    398 
    399 		if (lookupchar(charlist[i], font) != -1) {
    400 
    401 			font->glyphs[i] = calloc(1, sizeof(glyph_t));
    402 
    403 			if (!font->glyphs[i]) {
    404 				perror(NULL);
    405 				exit(EX_OSERR);
    406 			}
    407 
    408 			readchar(i, font->glyphs[i], font);
    409 
    410 		} else {
    411 			font->glyphs[i] = NULL;
    412 		}
    413 	}
    414 
    415 	return font;
    416 }
    417 
    418 void
    419 readchar(int i, glyph_t *glyph, font_t *font)
    420 {
    421 	if (font->charlist[i] == 0xffff) {
    422 		printf("char not found\n");
    423 		return;
    424 	}
    425 
    426 	uint8_t *p = font->data + font->charlist[i];
    427 
    428 	uint8_t ch;
    429 	uint8_t color;
    430 
    431 	glyph->width = *p;
    432 	p++;
    433 	glyph->height = *p;
    434 	p++;
    435 
    436 	int row = 0;
    437 	int col = 0;
    438 	int width = glyph->width;
    439 	int height = glyph->height;
    440 
    441 	if (height > font->height) {
    442 		font->height = height;
    443 	}
    444 
    445 	glyph->cell = calloc(width * font->height, sizeof(cell_t));
    446 	if (!glyph->cell) {
    447 		perror(NULL);
    448 		exit(EX_OSERR);
    449 	}
    450 
    451 	for (int i = 0; i < width * font->height; i++) {
    452 		glyph->cell[i].utfchar[0] = ' ';
    453 		glyph->cell[i].color = 0;
    454 	}
    455 
    456 	while (*p) {
    457 
    458 		ch = *p;
    459 		p++;
    460 
    461 
    462 		if (ch == '\r') {
    463 			ch = ' ';
    464 			row++;
    465 			col = 0;
    466 		} else {
    467 			color = *p;
    468 			p++;
    469 #ifdef DEBUG
    470 			if (ch == 0x09)
    471 				ch = 'T';
    472 			if (ch < 0x20)
    473 				ch = '?';
    474 #else
    475 			if (ch < 0x20)
    476 				ch = ' ';
    477 #endif /* DEBUG */
    478 			if (opt.encoding == ENC_UNICODE) {
    479 				ibmtoutf8((char *)&ch,
    480 					  glyph->cell[row * width + col].utfchar);
    481 			} else {
    482 				glyph->cell[row * width + col].utfchar[0] = ch;
    483 			}
    484 
    485 			glyph->cell[row * width + col].color = color;
    486 
    487 			col++;
    488 		}
    489 	}
    490 }
    491 
    492 int
    493 lookupchar(char c, const font_t *font)
    494 {
    495 	for (int i = 0; i < NUM_CHARS; i++) {
    496 		if (charlist[i] == c && font->charlist[i] != 0xffff)
    497 			return i;
    498 	}
    499 
    500 	return -1;
    501 }
    502 
    503 void
    504 ibmtoutf8(char *a, char *u)
    505 {
    506 	static iconv_t conv = (iconv_t)0;
    507 
    508 	size_t inchsize = 1;
    509 	size_t outchsize = MAX_UTFSTR;
    510 
    511 	if (!conv) {
    512 		conv = iconv_open("UTF-8", "CP437");
    513 	}
    514 
    515 	iconv(conv, &a, &inchsize, &u, &outchsize);
    516 
    517 	return;
    518 }
    519 
    520 void
    521 printcolor(uint8_t color)
    522 {
    523 
    524 	uint8_t fg = color & 0x0f;
    525 	uint8_t bg = (color & 0xf0) >> 4;
    526 
    527 	/* thedraw colors                                     BRT BRT BRT BRT BRT BRT BRT BRT */
    528 	/* thedraw colors     BLK BLU GRN CYN RED MAG BRN GRY BLK BLU GRN CYN RED PNK YLW WHT */
    529 	uint8_t fgacolors[] = {30, 34, 32, 36, 31, 35, 33, 37, 90, 94, 92, 96, 91, 95, 93, 97};
    530 	uint8_t bgacolors[] = {40, 44, 42, 46, 41, 45, 43, 47};
    531 	uint8_t fgmcolors[] = { 1,  2,  3, 10,  5,  6,  7, 15, 14,  12, 9, 11,  4, 13,  8,  0};
    532 	uint8_t bgmcolors[] = { 1,  2,  3, 10,  5,  6,  7, 15, 14,  12, 9, 11,  4, 13,  8,  0};
    533 
    534 	if (opt.color == COLOR_ANSI) {
    535 		printf("\x1b[");
    536 		printf("%d;", fgacolors[fg]);
    537 		printf("%dm", bgacolors[bg]);
    538 	} else {
    539 		printf("\x03");
    540 		printf("%d,", fgmcolors[fg]);
    541 		printf("%d", bgmcolors[bg]);
    542 	}
    543 }
    544 
    545 void
    546 printrow(const glyph_t *glyph, int row)
    547 {
    548 	char *utfchar;
    549 	uint8_t color;
    550 	int i;
    551 	uint8_t lastcolor;
    552 
    553 	for (i = 0; i < glyph->width; i++) {
    554 		utfchar = glyph->cell[glyph->width * row + i].utfchar;
    555 		color = glyph->cell[glyph->width * row + i].color;
    556 
    557 		if (i == 0 || color != lastcolor) {
    558 			printcolor(color);
    559 			lastcolor = color;
    560 		}
    561 
    562 		printf("%s", utfchar);
    563 	}
    564 
    565 	if (opt.color == COLOR_ANSI) {
    566 		printf("\x1b[0m");
    567 	} else {
    568 		printf("\x03");
    569 	}
    570 }
    571 
    572 void
    573 printstr(const char *str, font_t *font)
    574 {
    575 	int maxheight = 0;
    576 	int linewidth = 0;
    577 	int len = strlen(str);
    578 	int padding = 0;
    579 	int n = 0;
    580 
    581 	for (int i = 0; i < len; i++) {
    582 		glyph_t *g;
    583 
    584 		n = lookupchar(str[i], font);
    585 
    586 		if (n == -1) {
    587 			continue;
    588 		}
    589 
    590 		g = font->glyphs[n];
    591 
    592 		if (g->height > maxheight) {
    593 			maxheight = g->height;
    594 		}
    595 
    596 		linewidth += g->width;
    597 		if (linewidth + 1 < len) {
    598 			linewidth += font->spacing;
    599 		}
    600 	}
    601 
    602 	if (opt.justify == CENTER_JUSTIFY) {
    603 		padding = (opt.width - linewidth) / 2;
    604 	} else if (opt.justify == RIGHT_JUSTIFY) {
    605 		padding = (opt.width - linewidth);
    606 	}
    607 
    608 	for (int i = 0; i < maxheight; i++) {
    609 		for (int i = 0; i < padding; ++i) {
    610 			printf(" ");
    611 		}
    612 
    613 		for (int c = 0; c < strlen(str); c++) {
    614 			n = lookupchar(str[c], font);
    615 
    616 			if (n == -1) {
    617 				continue;
    618 			}
    619 
    620 			glyph_t *g = font->glyphs[n];
    621 			printrow(g, i);
    622 
    623 			if (opt.color == COLOR_ANSI) {
    624 				printf("\x1b[0m");
    625 			} else {
    626 				printf("\x03");
    627 			}
    628 
    629 			for (int s = 0; s < font->spacing; s++) {
    630 				printf(" ");
    631 			}
    632 		}
    633 
    634 		if (opt.color == COLOR_ANSI) {
    635 			printf("\x1b[0m\n");
    636 		} else {
    637 			printf("\x03\r\n");
    638 		}
    639 	}
    640 }