| /* |
| * dumpkeys.c |
| * |
| * derived from version 0.81 - aeb@cwi.nl |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <linux/types.h> |
| #include <linux/kd.h> |
| #include <linux/keyboard.h> |
| #include <sys/ioctl.h> |
| #include <string.h> |
| #include <errno.h> |
| #include "ksyms.h" |
| #include "getfd.h" |
| #include "modifiers.h" |
| #include "nls.h" |
| #include "version.h" |
| |
| #ifndef KT_LETTER |
| #define KT_LETTER KT_LATIN |
| #endif |
| |
| #ifndef MAX_NR_KEYMAPS |
| #define MAX_NR_KEYMAPS NR_KEYMAPS |
| #endif |
| |
| static int fd; |
| static int verbose; |
| static int nr_keys = 0; /* probably 112, 128, 256 or 512 */ |
| |
| int keymap_index[MAX_NR_KEYMAPS]; /* inverse of good_keymap */ |
| int good_keymap[MAX_NR_KEYMAPS], keymapnr, allocct; |
| |
| /* note: asking for n > 255 is not meaningful: ke.kb_index is uchar */ |
| static int |
| has_key(int n) { |
| struct kbentry ke; |
| |
| ke.kb_table = 0; /* plain map is always present */ |
| ke.kb_index = n; |
| return !ioctl(fd, KDGKBENT, (unsigned long)&ke); |
| } |
| |
| static void |
| find_nr_keys(void) { |
| nr_keys = (has_key(255) ? 256 : has_key(127) ? 128 : 112); |
| } |
| |
| static void |
| get_keymaps(void) { |
| int i, j; |
| struct kbentry ke; |
| |
| keymapnr = allocct = 0; |
| for (i=0; i<MAX_NR_KEYMAPS; i++) { |
| ke.kb_index = 0; |
| ke.kb_table = i; |
| j = ioctl(fd, KDGKBENT, (unsigned long)&ke); |
| if (j && errno != EINVAL) { |
| perror("KDGKBENT"); |
| fprintf(stderr, |
| _("KDGKBENT error at index 0 in table %d\n"), i); |
| exit(1); |
| } |
| if (!j && ke.kb_value != K_NOSUCHMAP) { |
| keymap_index[i] = keymapnr; |
| good_keymap[keymapnr++] = i; |
| if (ke.kb_value == K_ALLOCATED) |
| allocct++; |
| } else { |
| keymap_index[i] = -1; |
| } |
| } |
| if (keymapnr == 0) { |
| fprintf(stderr, _("%s: cannot find any keymaps?\n"), progname); |
| exit(1); |
| } |
| if (good_keymap[0] != 0) { |
| fprintf(stderr, |
| _("%s: plain map not allocated? very strange ...\n"), |
| progname); |
| /* this is not fatal */ |
| } |
| } |
| |
| static void |
| print_keymaps(void) { |
| int i,m0,m; |
| |
| printf("keymaps "); |
| for (i=0; i<keymapnr; i++) { |
| if (i) |
| printf(","); |
| m0 = m = good_keymap[i]; |
| while (i+1 < keymapnr && good_keymap[i+1] == m+1) |
| i++, m++; |
| if (m0 == m) |
| printf("%d", m0); |
| else |
| printf("%d-%d", m0, m); |
| } |
| printf("\n"); |
| } |
| |
| static int |
| get_bind(u_char index, u_char table) { |
| struct kbentry ke; |
| |
| ke.kb_index = index; |
| ke.kb_table = table; |
| if (ioctl(fd, KDGKBENT, (unsigned long)&ke)) { |
| perror("KDGKBENT"); |
| fprintf(stderr, _("KDGKBENT error at index %d in table %d\n"), |
| index, table); |
| exit(1); |
| } |
| return ke.kb_value; |
| } |
| |
| static void |
| print_keysym(int code, char numeric) { |
| int t; |
| int v; |
| const char *p; |
| |
| printf(" "); |
| t = KTYP(code); |
| v = KVAL(code); |
| if (t >= syms_size) { |
| code = code ^ 0xf000; |
| if (!numeric && (p = unicodetoksym(code)) != NULL) |
| printf("%-16s", p); |
| else |
| printf("U+%04x ", code); |
| return; |
| } |
| if (t == KT_LETTER) { |
| t = KT_LATIN; |
| printf("+"); |
| } |
| if (!numeric && t < syms_size && v < syms[t].size && |
| (p = syms[t].table[v])[0]) |
| printf("%-16s", p); |
| else if (!numeric && t == KT_META && v < 128 && v < syms[0].size && |
| (p = syms[0].table[v])[0]) |
| printf("Meta_%-11s", p); |
| else |
| printf("0x%04x ", code); |
| } |
| |
| static char |
| valid_type(int t) { |
| struct kbentry ke; |
| char status; |
| |
| ke.kb_index = 0; |
| ke.kb_table = 0; |
| ke.kb_value = K(t, 0); |
| status = (ioctl(fd, KDSKBENT, (unsigned long)&ke) == 0); |
| return status; |
| } |
| |
| static u_char |
| maximum_val(int t) { |
| struct kbentry ke, ke0; |
| int i; |
| |
| ke.kb_index = 0; |
| ke.kb_table = 0; |
| ke.kb_value = K_HOLE; |
| ke0 = ke; |
| ioctl(fd, KDGKBENT, (unsigned long)&ke0); |
| |
| for (i = 0; i < 256; i++) { |
| ke.kb_value = K(t, i); |
| if (ioctl(fd, KDSKBENT, (unsigned long)&ke)) |
| break; |
| } |
| ke.kb_value = K_HOLE; |
| ioctl(fd, KDSKBENT, (unsigned long)&ke0); |
| |
| return i - 1; |
| } |
| |
| #define NR_TYPES 15 |
| int maxval[NR_TYPES]; |
| |
| #ifdef KDGKBDIACR |
| /* isgraph() does not know about iso-8859; printing the character |
| unescaped makes the output easier to check. Maybe this should |
| be an option. Use locale? */ |
| static void |
| outchar (unsigned char c) { |
| printf("'"); |
| printf((c == '\'' || c == '\\') ? "\\%c" |
| : (isgraph(c) || c == ' ' || c >= 0200) ? "%c" |
| : "\\%03o", c); |
| printf("'"); |
| } |
| |
| static struct kbdiacrs kd; |
| |
| static void |
| get_diacs(void) { |
| static int got_diacs = 0; |
| |
| if(!got_diacs && ioctl(fd, KDGKBDIACR, (unsigned long)&kd)) { |
| perror("KDGKBDIACR"); |
| exit(1); |
| } |
| got_diacs = 1; |
| } |
| |
| static int |
| nr_of_diacs(void) { |
| get_diacs(); |
| return kd.kb_cnt; |
| } |
| |
| static void |
| dump_diacs(void) { |
| int i; |
| |
| get_diacs(); |
| for (i = 0; i < kd.kb_cnt; i++) { |
| printf("compose "); |
| outchar(kd.kbdiacr[i].diacr); |
| printf(" "); |
| outchar(kd.kbdiacr[i].base); |
| printf(" to "); |
| outchar(kd.kbdiacr[i].result); |
| printf("\n"); |
| } |
| } |
| #endif |
| |
| static void |
| show_short_info(void) { |
| int i; |
| |
| printf(_("keycode range supported by kernel: 1 - %d\n"), |
| nr_keys - 1); |
| printf(_("max number of actions bindable to a key: %d\n"), |
| MAX_NR_KEYMAPS); |
| get_keymaps(); |
| printf(_("number of keymaps in actual use: %d\n"), |
| keymapnr); |
| if (allocct) |
| printf(_("of which %d dynamically allocated\n"), allocct); |
| printf(_("ranges of action codes supported by kernel:\n")); |
| for (i = 0; i < NR_TYPES && valid_type(i); i++) { |
| maxval[i] = maximum_val(i); |
| printf(" 0x%04x - 0x%04x\n", K(i, 0), K(i, maxval[i])); |
| } |
| printf(_("number of function keys supported by kernel: %d\n"), |
| MAX_NR_FUNC); |
| |
| printf(_("max nr of compose definitions: %d\n"), |
| MAX_DIACR); |
| printf(_("nr of compose definitions in actual use: %d\n"), |
| nr_of_diacs()); |
| } |
| |
| static struct { |
| char *name; |
| int bit; |
| } modifiers[] = { |
| { "shift", KG_SHIFT }, |
| { "altgr", KG_ALTGR }, |
| { "control",KG_CTRL }, |
| { "alt", KG_ALT }, |
| { "shiftl", KG_SHIFTL }, |
| { "shiftr", KG_SHIFTR }, |
| { "ctrll", KG_CTRLL }, |
| { "ctrlr", KG_CTRLR } |
| }; |
| |
| static void |
| dump_symbols(void) { |
| int t; |
| int v; |
| const char *p; |
| |
| printf(_("Symbols recognized by %s:\n(numeric value, symbol)\n\n"), |
| progname); |
| for (t = 0; t < syms_size; t++) { |
| if (syms[t].size) { |
| for (v = 0; v < syms[t].size; v++) |
| if ((p = syms[t].table[v])[0]) |
| printf("0x%04x\t%s\n", K(t, v), p); |
| } else if (t == KT_META) { |
| for (v = 0; v < syms[0].size && v < 128; v++) |
| if ((p = syms[0].table[v])[0]) |
| printf("0x%04x\tMeta_%s\n", K(t, v), p); |
| } |
| } |
| printf(_("\nThe following synonyms are recognized:\n\n")); |
| for (t = 0; t < syn_size; t++) |
| printf(_("%-15s for %s\n"), synonyms[t].synonym, |
| synonyms[t].official_name); |
| printf(_("\nRecognized modifier names and their column numbers:\n")); |
| for (t = 0; t < sizeof(modifiers)/sizeof(modifiers[0]); t++) |
| printf("%s\t\t%3d\n", modifiers[t].name, 1 << modifiers[t].bit); |
| } |
| |
| static void |
| print_mod(int x) { |
| int t; |
| |
| if (!x) |
| printf("plain\t"); |
| else |
| for (t = 0; t < sizeof(modifiers)/sizeof(modifiers[0]); t++) |
| if (x & (1 << modifiers[t].bit)) |
| printf("%s\t", modifiers[t].name); |
| } |
| |
| static void |
| print_bind(int bufj, int i, int j, char numeric) { |
| if(j) |
| printf("\t"); |
| print_mod(j); |
| printf("keycode %3d =", i); |
| print_keysym(bufj, numeric); |
| printf("\n"); |
| } |
| |
| #define DEFAULT 0 |
| #define FULL_TABLE 1 /* one line for each keycode */ |
| #define SEPARATE_LINES 2 /* one line for each (modifier,keycode) pair */ |
| #define UNTIL_HOLE 3 /* one line for each keycode, until 1st hole */ |
| |
| static void |
| dump_keys(char table_shape, char numeric) { |
| int i, j, k; |
| int buf[MAX_NR_KEYMAPS]; |
| int isletter, islatin, isasexpected; |
| int typ, val; |
| int alt_is_meta = 0; |
| int zapped[MAX_NR_KEYMAPS]; |
| |
| get_keymaps(); |
| print_keymaps(); |
| if (!keymapnr) |
| return; |
| |
| if (table_shape == FULL_TABLE || table_shape == SEPARATE_LINES) |
| goto no_shorthands; |
| |
| /* first pass: determine whether to set alt_is_meta */ |
| for (j = 0; j < MAX_NR_KEYMAPS; j++) { |
| int ja = (j | M_ALT); |
| if (j != ja && keymap_index[j] >= 0 && keymap_index[ja] >= 0) |
| for (i = 1; i < nr_keys; i++) { |
| int buf0, buf1, type; |
| |
| buf0 = get_bind(i, j); |
| type = KTYP(buf0); |
| if ((type == KT_LATIN || type == KT_LETTER) |
| && KVAL(buf0) < 128) { |
| buf1 = get_bind(i, ja); |
| if (buf1 != K(KT_META, KVAL(buf0))) { |
| if (verbose) { |
| printf(_("# not alt_is_meta: " |
| "on keymap %d key %d is bound to"), |
| ja, i); |
| print_keysym(buf1, numeric); |
| printf("\n"); |
| } |
| goto not_alt_is_meta; |
| } |
| } |
| } |
| } |
| alt_is_meta = 1; |
| printf("alt_is_meta\n"); |
| not_alt_is_meta: |
| |
| no_shorthands: |
| for (i = 1; i < nr_keys; i++) { |
| for (j = 0; j < keymapnr; j++) |
| buf[j] = get_bind(i, good_keymap[j]); |
| |
| if (table_shape == FULL_TABLE) { |
| printf("keycode %3d =", i); |
| for (j = 0; j < keymapnr; j++) |
| print_keysym(buf[j], numeric); |
| printf("\n"); |
| continue; |
| } |
| |
| if (table_shape == SEPARATE_LINES) { |
| for (j = 0; j < keymapnr; j++) |
| print_bind(buf[j], i, good_keymap[j], numeric); |
| printf("\n"); |
| continue; |
| } |
| |
| typ = KTYP(buf[0]); |
| val = KVAL(buf[0]); |
| islatin = (typ == KT_LATIN || typ == KT_LETTER); |
| isletter = (islatin && |
| ((val >= 'A' && val <= 'Z') || |
| (val >= 'a' && val <= 'z'))); |
| isasexpected = 0; |
| if (isletter) { |
| u_short defs[16]; |
| defs[0] = K(KT_LETTER, val); |
| defs[1] = K(KT_LETTER, val ^ 32); |
| defs[2] = defs[0]; |
| defs[3] = defs[1]; |
| for(j=4; j<8; j++) |
| defs[j] = K(KT_LATIN, val & ~96); |
| for(j=8; j<16; j++) |
| defs[j] = K(KT_META, KVAL(defs[j-8])); |
| |
| for(j = 0; j < keymapnr; j++) { |
| k = good_keymap[j]; |
| if ((k >= 16 && buf[j] != K_HOLE) || (k < 16 && buf[j] != defs[k])) |
| goto unexpected; |
| } |
| isasexpected = 1; |
| } |
| unexpected: |
| |
| /* wipe out predictable meta bindings */ |
| for (j = 0; j < keymapnr; j++) |
| zapped[j] = 0; |
| if (alt_is_meta) { |
| for(j = 0; j < keymapnr; j++) { |
| int ka, ja, typ; |
| k = good_keymap[j]; |
| ka = (k | M_ALT); |
| ja = keymap_index[ka]; |
| if (k != ka && ja >= 0 |
| && ((typ=KTYP(buf[j])) == KT_LATIN || typ == KT_LETTER) |
| && KVAL(buf[j]) < 128) { |
| if (buf[ja] != K(KT_META, KVAL(buf[j]))) |
| fprintf(stderr, _("impossible: not meta?\n")); |
| buf[ja] = K_HOLE; |
| zapped[ja] = 1; |
| } |
| } |
| } |
| |
| printf("keycode %3d =", i); |
| if (isasexpected) { |
| /* print only a single entry */ |
| /* suppress the + for ordinary a-zA-Z */ |
| print_keysym(K(KT_LATIN, val), numeric); |
| printf("\n"); |
| } else { |
| /* choose between single entry line followed by exceptions, |
| and long line followed by exceptions; avoid VoidSymbol */ |
| int bad = 0; |
| int count = 0; |
| for(j = 1; j < keymapnr; j++) if (!zapped[j]) { |
| if (buf[j] != buf[0]) |
| bad++; |
| if (buf[j] != K_HOLE) |
| count++; |
| } |
| if (bad <= count && bad < keymapnr-1) { |
| if (buf[0] != K_HOLE) |
| print_keysym(buf[0], numeric); |
| printf("\n"); |
| for (j = 1; j < keymapnr; j++) |
| if (buf[j] != buf[0] && !zapped[j]) |
| print_bind(buf[j], i, good_keymap[j], numeric); |
| } else { |
| for (j = 0; j < keymapnr && buf[j] != K_HOLE && |
| (j == 0 || table_shape != UNTIL_HOLE || |
| good_keymap[j] == good_keymap[j-1]+1); j++) |
| print_keysym(buf[j], numeric); |
| printf("\n"); |
| for ( ; j < keymapnr; j++) |
| if (buf[j] != K_HOLE) |
| print_bind(buf[j], i, good_keymap[j], numeric); |
| } |
| } |
| } |
| } |
| |
| static void |
| dump_funcs(void) { |
| int i; |
| struct kbsentry fbuf; |
| char *p; |
| |
| for (i = 0; i < MAX_NR_FUNC; i++) { |
| fbuf.kb_func = i; |
| if (ioctl(fd, KDGKBSENT, (unsigned long)&fbuf)) { |
| if (errno == EINVAL && i > 0) /* an old kernel */ |
| break; |
| perror("KDGKBSENT"); |
| fprintf(stderr, _("KDGKBSENT failed at index %d: "), i); |
| exit(1); |
| } |
| if (!fbuf.kb_string[0]) |
| continue; |
| printf("string %s = \"", syms[KT_FN].table[i]); |
| for (p = fbuf.kb_string; *p; p++) { |
| if (*p == '"' || *p == '\\') { |
| putchar('\\'); putchar(*p); |
| } else if (isgraph(*p) || *p == ' ') |
| putchar(*p); |
| else |
| printf("\\%03o", *p); |
| } |
| printf("\"\n"); |
| } |
| } |
| |
| static void |
| usage(void) { |
| fprintf(stderr, _("dumpkeys version %s"), VERSION); |
| fprintf(stderr, _("\ |
| \n\ |
| usage: dumpkeys [options...]\n\ |
| \n\ |
| valid options are:\n\ |
| \n\ |
| -h --help display this help text\n\ |
| -i --short-info display information about keyboard driver\n\ |
| -l --long-info display above and symbols known to loadkeys\n\ |
| -n --numeric display keytable in hexadecimal notation\n\ |
| -f --full-table don't use short-hand notations, one row per keycode\n\ |
| -1 --separate-lines one line per (modifier,keycode) pair\n\ |
| --funcs-only display only the function key strings\n\ |
| --keys-only display only key bindings\n\ |
| --compose-only display only compose key combinations\n\ |
| -c --charset=")); |
| list_charsets(stderr); |
| fprintf(stderr, _("\ |
| interpret character action codes to be from the\n\ |
| specified character set\n\ |
| ")); |
| exit(1); |
| } |
| |
| int |
| main (int argc, char *argv[]) { |
| const char *short_opts = "hilvsnf1S:c:V"; |
| const struct option long_opts[] = { |
| { "help", no_argument, NULL, 'h' }, |
| { "short-info", no_argument, NULL, 'i' }, |
| { "long-info", no_argument, NULL, 'l' }, |
| { "numeric", no_argument, NULL, 'n' }, |
| { "full-table", no_argument, NULL, 'f' }, |
| { "separate-lines",no_argument, NULL, '1' }, |
| { "shape", required_argument, NULL, 'S' }, |
| { "funcs-only", no_argument, NULL, 't' }, |
| { "keys-only", no_argument, NULL, 'k' }, |
| { "compose-only",no_argument, NULL, 'd' }, |
| { "charset", required_argument, NULL, 'c' }, |
| { "verbose", no_argument, NULL, 'v' }, |
| { "version", no_argument, NULL, 'V' }, |
| { NULL, 0, NULL, 0 } |
| }; |
| int c; |
| char long_info = 0; |
| char short_info = 0; |
| char numeric = 0; |
| char table_shape = 0; |
| char funcs_only = 0; |
| char keys_only = 0; |
| char diac_only = 0; |
| |
| set_progname(argv[0]); |
| |
| setlocale(LC_ALL, ""); |
| bindtextdomain(PACKAGE, LOCALEDIR); |
| textdomain(PACKAGE); |
| |
| while ((c = getopt_long(argc, argv, |
| short_opts, long_opts, NULL)) != -1) { |
| switch (c) { |
| case 'i': |
| short_info = 1; |
| break; |
| case 's': |
| case 'l': |
| long_info = 1; |
| break; |
| case 'n': |
| numeric = 1; |
| break; |
| case 'f': |
| table_shape = FULL_TABLE; |
| break; |
| case '1': |
| table_shape = SEPARATE_LINES; |
| break; |
| case 'S': |
| table_shape = atoi(optarg); |
| break; |
| case 't': |
| funcs_only = 1; |
| break; |
| case 'k': |
| keys_only = 1; |
| break; |
| case 'd': |
| diac_only = 1; |
| break; |
| case 'v': |
| verbose = 1; |
| break; |
| case 'c': |
| if ((set_charset(optarg)) != 0) |
| usage(); |
| printf("charset \"%s\"\n", optarg); |
| break; |
| case 'V': |
| print_version_and_exit(); |
| case 'h': |
| case '?': |
| usage(); |
| } |
| } |
| |
| if (optind < argc) |
| usage(); |
| |
| fd = getfd(NULL); |
| |
| find_nr_keys(); |
| |
| if (short_info || long_info) { |
| show_short_info(); |
| if (long_info) |
| dump_symbols(); |
| exit(0); |
| } |
| |
| #ifdef KDGKBDIACR |
| if (!diac_only) { |
| #endif |
| if (!funcs_only) |
| dump_keys(table_shape, numeric); |
| if (!keys_only) |
| dump_funcs(); |
| #ifdef KDGKBDIACR |
| } |
| if (!funcs_only && !keys_only) |
| dump_diacs(); |
| #endif |
| |
| exit(0); |
| } |