#include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include static void usage(const char* argv){ fprintf(stderr, "usage: %s [ ttydev ]\n", argv); } static int get_tty_fd(const char* dev){ if(dev == NULL){ dev = "/dev/tty"; } int fd = open(dev, O_RDWR | O_CLOEXEC); if(fd < 0){ fprintf(stderr, "Error opening %s (%s)\n", dev, strerror(errno)); } return fd; } static bool is_linux_console(int fd){ int mode, r; if( (r = ioctl(fd, KDGETMODE, &mode)) ){ fprintf(stderr, "Not a Linux console, KDGETMODE failed (%s)\n", strerror(errno)); return false; } printf("Verified Linux console, %s mode\n", mode == KD_TEXT ? "text" : mode == KD_GRAPHICS ? "graphics" : "unknown"); return true; } static int get_linux_colormap(int fd){ unsigned char cmap[48]; if(ioctl(fd, GIO_CMAP, cmap)){ fprintf(stderr, "Error reading Linux colormap (%s)\n", strerror(errno)); return -1; } for(size_t i = 0 ; i < sizeof(cmap) / 12 ; ++i){ const int c1 = i * 4; const int c2 = c1 + 1; const int c3 = c2 + 1; const int c4 = c3 + 1; printf(" |%02d| %3u %3u %3u |%02d| %3u %3u %3u |%02d| %3u %3u %3u |%02d| %3u %3u %3u\n", c1, cmap[c1 * 3], cmap[c1 * 3 + 1], cmap[c1 * 3 + 2], c2, cmap[c2 * 3], cmap[c2 * 3 + 1], cmap[c2 * 3 + 2], c3, cmap[c3 * 3], cmap[c3 * 3 + 1], cmap[c3 * 3 + 2], c4, cmap[c4 * 3], cmap[c4 * 3 + 1], cmap[c4 * 3 + 2]); } return 0; } // FIXME assumes a width of 8. it is apparently possible to have widths // other than 8, but they don't work properly with GIO_FONTX according to // the showconsolefont source code. use the KDFONTOP ioctl to learn true // font width. static int explode_glyph_row(const unsigned char** row){ printf("%s%s%s%s%s%s%s%s ", **row & 0x80 ? "*": " ", **row & 0x40 ? "*": " ", **row & 0x20 ? "*": " ", **row & 0x10 ? "*": " ", **row & 0x08 ? "*": " ", **row & 0x04 ? "*": " ", **row & 0x02 ? "*": " ", **row & 0x01 ? "*": " "); ++*row; return 0; } static int get_linux_consolefont(int fd, unsigned showglyphs){ struct consolefontdesc cfd = {}; cfd.charcount = 512; cfd.chardata = malloc(32 * cfd.charcount); if(cfd.chardata == NULL){ return -1; } if(ioctl(fd, GIO_FONTX, &cfd)){ fprintf(stderr, "Error reading Linux kernelfont (%s)\n", strerror(errno)); free(cfd.chardata); return -1; } printf("Kernel font size (glyphcount): %hu\n", cfd.charcount); printf("Kernel font character height: %hu\n", cfd.charheight); if(cfd.charcount > 512){ fprintf(stderr, "Warning: kernel returned excess charcount\n"); free(cfd.chardata); return -1; } if(showglyphs){ for(unsigned i = 0 ; i < cfd.charcount ; i += 7){ const unsigned char* g1 = (unsigned char*)cfd.chardata + 32 * i; const unsigned char* g2 = g1 + 32; const unsigned char* g3 = g2 + 32; const unsigned char* g4 = g3 + 32; const unsigned char* g5 = g4 + 32; const unsigned char* g6 = g5 + 32; const unsigned char* g7 = g6 + 32; for(unsigned row = 0 ; row < cfd.charheight ; ++row){ explode_glyph_row(&g1); if(i < cfd.charcount - 1u){ explode_glyph_row(&g2); if(i < cfd.charcount - 2u){ explode_glyph_row(&g3); if(i < cfd.charcount - 3u){ explode_glyph_row(&g4); if(i < cfd.charcount - 4u){ explode_glyph_row(&g5); if(i < cfd.charcount - 5u){ explode_glyph_row(&g6); if(i < cfd.charcount - 6u){ explode_glyph_row(&g7); } } } } } } printf("\n"); } } } free(cfd.chardata); return 0; } static int get_linux_consolemap(int fd){ struct unimapdesc map = {}; map.entry_ct = USHRT_MAX; map.entries = malloc(map.entry_ct * sizeof(struct unipair)); if(ioctl(fd, GIO_UNIMAP, &map)){ fprintf(stderr, "Error reading Unicode->font map (%s)\n", strerror(errno)); free(map.entries); return -1; } printf("Unicode->font entries: %hu\n", map.entry_ct); for(int i = 0 ; i < map.entry_ct ; ++i){ wchar_t w = map.entries[i].unicode; printf(" %05hx (%lc)->%3hu ", map.entries[i].unicode, iswprint(w) ? w : L' ', map.entries[i].fontpos); if(i % 4 == 3){ printf("\n"); } } free(map.entries); if(map.entry_ct % 4){ printf("\n"); } return 0; } static int get_linux_unimap(int fd){ unsigned short map[E_TABSZ]; if(ioctl(fd, GIO_UNISCRNMAP, map)){ fprintf(stderr, "Error reading Unicode->screen map (%s)\n", strerror(errno)); return -1; } printf("Unicode->screen entries: %d\n", E_TABSZ); int standard = 0; for(size_t i = 0 ; i < sizeof(map) / sizeof(*map) ; ++i){ if(map[i] != 0xf000 + i){ printf(" |%03zu| %hx\n", i, map[i]); }else{ ++standard; } } printf(" %d/%d direct-to-font mapping%s\n", standard, E_TABSZ, standard == 1 ? "" : "s"); return 0; } int main(int argc, char** argv){ if(setlocale(LC_ALL, "") == NULL){ fprintf(stderr, "Error setting locale\n"); return EXIT_FAILURE; } if(argc > 2){ usage(argv[0]); return EXIT_FAILURE; } int fd = get_tty_fd(argv[1]); if(fd < 0){ return EXIT_FAILURE; } if(!is_linux_console(fd)){ return EXIT_FAILURE; } int r = 0; r |= get_linux_colormap(fd); r |= get_linux_consolefont(fd, true); r |= get_linux_consolemap(fd); r |= get_linux_unimap(fd); if(close(fd)){ fprintf(stderr, "Error closing %d (%s)\n", fd, strerror(errno)); } return r ? EXIT_FAILURE : EXIT_SUCCESS; } #else int main(void){ fprintf(stderr, "This is a Linux-only program\n"); return EXIT_FAILURE; } #endif