/* showfont.c * * Copyright (C) 2004 Andy Goth * http://ioioio.net/ * This program is available under the GNU General Public License. */ #define _GNU_SOURCE #include #include #include #include #include /* Usage information. */ #define USAGE_STR "Usage: showfont [\e[1mOPTIONS\e[0m]\n" \ "Fashionably display the current terminal font.\n" \ "\n" \ " -r, --range=\e[1mBEG\e[0m[,\e[1mEND\e[0m] Display character[s] number "\ "\e[1mBEG\e[0m [through \e[1mEND\e[0m].\n" \ " -c, --columns=\e[1mNUM\e[0m Display \e[1mNUM\e[0m characters " \ "per row.\n" \ " -g, --grid=\e[1mPRI\e[0m[,\e[1mSEC\e[0m] Group \e[1mPRI\e[0m " \ "characters [also group \e[1mSEC\e[0m groups].\n" \ " -h, --hex-head[=\e[1mBOOL\e[0m] Enable or disable hexadecimal row " \ "headers.\n" \ " -d, --dec-head[=\e[1mBOOL\e[0m] Enable or disable decimal row " \ "headers.\n" \ " -n, --no-newline[=\e[1mBOOL\e[0m] Enable or disable suppression of " \ "final newline.\n" \ " -C, --colors[=\e[1mCOL1\e[0m,\e[1mCOL2\e[0m] Enable color [set column " \ "colors to \e[1mCOL1\e[0m, \e[1mCOL2\e[0m].\n" \ " -H, --help Print this message and exit.\n" \ " -V, --version Print version information and then exit.\n" \ "\n" \ "0: \e[1;30mgray\e[0m; 1: \e[1;31mred\e[0m; 2: \e[1;32mgreen\e[0m; " \ "3: \e[1;33myellow\e[0m; 4: \e[1;34mblue\e[0m; 5: \e[1;35mpink\e[0m; " \ "6: \e[1;36mcyan\e[0m; 7: \e[1;37mwhite\e[0m.\n" /* Yeah, there are too many options, they control all the wrong things, it's * not very robust, and the result is inflexible and overcomplicated, but oh * well, so long as you stick to the paved surfaces everything's reasonably * pretty. I learned that trick from Microsoft. :^) */ /* Version information. */ #define VERSION_STR "showfont 0.1, March 21, 2004\n" \ "Copyright 2004 Andy Goth .\n" \ "Check http://ioioio.net/ for updates, if I ever release them. :^)\n" \ "showfont is free software, covered by the GNU General Public License;\n" \ "you are welcome to change and/or distribute it under certain conditions.\n" \ "There is absolutely no warranty for showfont. See the GPL for details.\n" /* An ill omen. */ #define ERROR_STR "Try `showfont --help' for more information.\n" /* Why don't I just use printf()/fprintf()? No reason. :^) I suppose I was * just in a do-it-yourself mood. */ /* Prints a string. */ #define print_str(s) write(1, (s), sizeof(s)) /* Prints a string to stderr. */ #define error_str(s) write(2, (s), sizeof(s)) /* Prints a character. */ #define print_byte(c) ({char x = c; write(1, &x, 1);}) /* Converts the low-order four bits of an integer to a hexadecimal character. */ #define to_hex(v) ({int x = (v) % 16; (x) > 9 ? (x) + 'a' - 10 : (x) + '0';}) /* Evaluates to true if the argument, a boolean string, is true. */ #define is_true(s) (strcasecmp((s), "on" ) == 0 || \ strcasecmp((s), "yes" ) == 0 || \ strcasecmp((s), "true") == 0 || \ strcasecmp((s), "1" ) == 0) /* Prints a nonnegative hexadecimal integer. */ static void print_hex(int v, int n) { while (n > 0) { --n; print_byte(to_hex(v >> (4 * n))); } } /* Prints a nonnegative decimal integer. */ static void print_dec(int v, int n) { char buf[n]; while (n > 0) { /* Calculate digits in order of increasing significance. */ --n; buf[n] = v % 10 + '0'; v /= 10; } /* Then print in order of decreasing significance. */ print_str(buf); } /* Prints any character from the current font. */ static void print_char(int c) { if (c < 32 || c > 126) { /* Encode character as UTF-8. */ char buf[3] = {0xef}; buf[1] = 0x80 | ((c >> 6) & 7); buf[2] = 0x80 | ((c >> 0) & 63); print_str(buf); } else { /* Send character directly. */ print_byte(c); } } /* This function needs no introduction. */ int main(int argc, char** argv) { /* Defaults for command-line options. */ int first_char = 0, last_char = 255, num_cols = 64; int pri_grid = 4, sec_grid = 8, hex_headers = 0; int dec_headers = 0, no_newline = 0, colors = 0; int color_1 = 1, color_2 = 4; /* getopt_long parameters describing command-line options. */ char* short_opts = "r:c:g:h::d::n::C::HV"; struct option long_opts[] = { {"range" , required_argument, NULL, 'r'}, {"columns" , required_argument, NULL, 'c'}, {"grid" , required_argument, NULL, 'g'}, {"hex-head" , optional_argument, NULL, 'h'}, {"dec-head" , optional_argument, NULL, 'd'}, {"no-newline" , optional_argument, NULL, 'n'}, {"colors" , optional_argument, NULL, 'C'}, {"help" , no_argument , NULL, 'H'}, {"version" , no_argument , NULL, 'V'}, {NULL , 0 , NULL, 0 } }; int c; char* p; /* Scan the command line. */ while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch (c) { case 'r': first_char = atoi(optarg); p = strchr(optarg, ','); last_char = p == NULL ? first_char : atoi(p + 1); if (first_char < 0 ) first_char = 0; if (first_char > 511 ) first_char = 511; if (last_char < first_char) last_char = first_char; if (last_char > 511 ) last_char = 511; break; case 'c': num_cols = atoi(optarg); break; case 'g': pri_grid = atoi(optarg); p = strchr(optarg, ','); if (p != NULL) { sec_grid = atoi(p + 1); } else { sec_grid = 0; } break; case 'h': hex_headers = optarg == NULL ? 1 : is_true(optarg); break; case 'd': dec_headers = optarg == NULL ? 1 : is_true(optarg); break; case 'n': no_newline = optarg == NULL ? 1 : is_true(optarg); break; case 'C': colors = 1; if (optarg != NULL) { color_1 = atoi(optarg); p = strchr(optarg, ','); color_2 = p == NULL ? color_1 : atoi(p + 1); } break; case 'H': print_str(USAGE_STR); exit(EXIT_SUCCESS); case 'V': print_str(VERSION_STR); exit(EXIT_SUCCESS); case '?': case ':': error_str(ERROR_STR); exit(EXIT_FAILURE); } } /* Enable Unicode mode. */ print_str("\e%G"); /* Plot the selected character range in a nice colored grid arrangement. */ for (c = first_char; c <= last_char; ++c) { if ((c - first_char) % num_cols == 0) { /* If this is the first character of the row... */ if (c != first_char) { /* ... but not the first row, go to the next line. */ if (colors) print_str("\e[0m"); print_byte('\n'); } if (hex_headers || dec_headers) { /* Print the left parens of the row header. */ if (colors) print_str("\e[30;1m"); print_byte('('); if (colors) print_str("\e[0m"); if (hex_headers) { /* Print the hex header. */ print_hex(c, 3); if (dec_headers) { /* Print the divider between the hex and dec headers. */ if (colors) print_str("\e[30;1m"); print_byte('/'); if (colors) print_str("\e[0m"); } } if (dec_headers) { /* Print the dec header. */ print_dec(c, 3); } /* Print the right parens of the row header. */ if (colors) print_str("\e[30;1m"); print_byte(')'); if (colors) print_str("\e[0m"); print_str(" "); } if (colors) { print_str("\e[3"); print_dec(color_1, 1); print_str(";1m"); } } else if (pri_grid > 0 && (c - first_char) % num_cols % pri_grid == 0) { /* If a grid line belongs here... */ if (colors) { if (sec_grid > 0 && (c - first_char) % num_cols % (pri_grid * sec_grid) == 0) { print_str("\e[0m"); } if ((c - first_char) % num_cols % (pri_grid * 2) == 0) { print_str("\e[3"); print_dec(color_1, 1); print_str(";1m"); } else { print_str("\e[3"); print_dec(color_2, 1); print_str(";1m"); } } print_byte(' '); if (sec_grid > 0 && (c - first_char) % num_cols % (pri_grid * sec_grid) == 0 && (c - first_char) % num_cols > 0) { print_byte(' '); } } print_char(c); } if (colors) print_str("\e[0m"); if (!no_newline) print_byte('\n'); /* Disable Unicode mode. (Hopefully it wasn't enabled before??) */ print_str("\e%@"); return EXIT_SUCCESS; } /* vim: set ts=4 sts=4 sw=4 et: */