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 }