| /* loadkeys.c |
| * |
| * This file is part of kbd project. |
| * Copyright (C) 1993 Risto Kankkunen. |
| * Copyright (C) 1993 Eugene G. Crosser. |
| * Copyright (C) 1994-2007 Andries E. Brouwer. |
| * Copyright (C) 2007-2012 Alexey Gladkov <gladkov.alexey@gmail.com> |
| * |
| * This file is covered by the GNU General Public License, |
| * which should be included with kbd as the file COPYING. |
| */ |
| #include "config.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <getopt.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <sysexits.h> |
| #include <sys/ioctl.h> |
| |
| #include <keymap.h> |
| |
| #include "libcommon.h" |
| |
| #ifdef USE_XKB |
| #include "xkbsupport.h" |
| #endif |
| |
| static const char *const dirpath1[] = { |
| DATADIR "/" KEYMAPDIR "/**", |
| KERNDIR "/", |
| NULL |
| }; |
| static const char *const suffixes[] = { |
| "", |
| ".kmap", |
| ".map", |
| NULL |
| }; |
| |
| static void KBD_ATTR_NORETURN |
| usage(int rc, const struct kbd_help *options) |
| { |
| fprintf(stderr, _("Usage: %s [option...] [mapfile...]\n"), |
| program_invocation_short_name); |
| |
| print_options(options); |
| |
| fprintf(stderr, "\n"); |
| fprintf(stderr, _("Default keymap: %s\n"), DEFMAP); |
| |
| fprintf(stderr, _("Available shapes:\n" |
| " 2 - default output;\n" |
| " 4 - one line for each keycode;\n" |
| " 8 - one line for each (modifier,keycode) pair;\n" |
| " 16 - one line for each keycode until 1st hole.\n" |
| )); |
| |
| print_report_bugs(); |
| |
| exit(rc); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int options = 0; |
| lk_table_shape table_shape = LK_SHAPE_DEFAULT; |
| |
| const char *const *dirpath; |
| const char *dirpath2[] = { NULL, NULL }; |
| |
| struct lk_ctx *ctx; |
| lk_flags flags = 0; |
| |
| int c, i, rc = -1; |
| int fd = -1; |
| int kbd_mode; |
| int kd_mode; |
| char *console = NULL; |
| char *ev; |
| struct kbdfile_ctx *fctx; |
| struct kbdfile *fp = NULL; |
| |
| #ifdef USE_XKB |
| struct xkeymap_params xkeymap_params = { |
| .model = "pc104", |
| .layout = NULL, |
| .variant = NULL, |
| .options = NULL, |
| .locale = NULL, |
| }; |
| int use_xkb = 0; |
| #endif |
| |
| setuplocale(); |
| |
| const char *const short_opts = "abcC:dhmpst::uqvV"; |
| const struct option long_opts[] = { |
| { "console", required_argument, NULL, 'C' }, |
| { "ascii", no_argument, NULL, 'a' }, |
| { "bkeymap", no_argument, NULL, 'b' }, |
| { "clearcompose", no_argument, NULL, 'c' }, |
| { "default", no_argument, NULL, 'd' }, |
| { "help", no_argument, NULL, 'h' }, |
| { "mktable", no_argument, NULL, 'm' }, |
| { "parse", no_argument, NULL, 'p' }, |
| { "clearstrings", no_argument, NULL, 's' }, |
| { "tkeymap", optional_argument, NULL, 't' }, |
| { "unicode", no_argument, NULL, 'u' }, |
| { "quiet", no_argument, NULL, 'q' }, |
| { "verbose", no_argument, NULL, 'v' }, |
| { "version", no_argument, NULL, 'V' }, |
| #ifdef USE_XKB |
| //{ "xkb-rules", required_argument, NULL, 1 }, |
| { "xkb-model", required_argument, NULL, 2 }, |
| { "xkb-layout", required_argument, NULL, 3 }, |
| { "xkb-variant", required_argument, NULL, 4 }, |
| { "xkb-options", required_argument, NULL, 5 }, |
| { "xkb-locale", required_argument, NULL, 6 }, |
| #endif |
| { NULL, 0, NULL, 0 } |
| }; |
| const struct kbd_help opthelp[] = { |
| #ifdef USE_XKB |
| { "--xkb-model=STR", _("Specifies model used to choose component names.") }, |
| { "--xkb-layout=STR", _("Specifies layout used to choose component names.") }, |
| { "--xkb-variant=STR", _("Specifies layout variant used to choose component names.") }, |
| { "--xkb-options=STR", _("Adds an option used to choose component names.") }, |
| { "--xkb-locale=LOCALE", _("Use LOCALE to search an appropriate compose file.") }, |
| #endif |
| { "-C, --console=DEV", _("the console device to be used.") }, |
| { "-a, --ascii", _("force conversion to ASCII.") }, |
| { "-b, --bkeymap", _("output a binary keymap to stdout.") }, |
| { "-c, --clearcompose", _("clear kernel compose table.") }, |
| { "-d, --default", _("load default.") }, |
| { "-m, --mktable", _("output a 'defkeymap.c' to stdout.") }, |
| { "-p, --parse", _("search and parse keymap without action.") }, |
| { "-s, --clearstrings", _("clear kernel string table.") }, |
| { "-u, --unicode", _("force conversion to Unicode.") }, |
| { "-q, --quiet", _("suppress all normal output.") }, |
| { "-t, --tkeymap[=SHAPE]", _("output a text keymap to stdout.") }, |
| { "-v, --verbose", _("be more verbose.") }, |
| { "-V, --version", _("print version number.") }, |
| { "-h, --help", _("print this usage message.") }, |
| { NULL, NULL } |
| }; |
| |
| enum options { |
| OPT_A = (1 << 1), |
| OPT_B = (1 << 2), |
| OPT_D = (1 << 3), |
| OPT_M = (1 << 4), |
| OPT_U = (1 << 5), |
| OPT_P = (1 << 6), |
| OPT_T = (1 << 7) |
| }; |
| |
| ctx = lk_init(); |
| if (!ctx) { |
| exit(EXIT_FAILURE); |
| } |
| |
| if (!(fctx = kbdfile_context_new())) |
| kbd_error(EXIT_FAILURE, errno, _("Unable to create kbdfile context")); |
| |
| while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { |
| switch (c) { |
| #ifdef USE_XKB |
| case 2: |
| xkeymap_params.model = optarg; |
| use_xkb = 1; |
| break; |
| case 3: |
| xkeymap_params.layout = optarg; |
| use_xkb = 1; |
| break; |
| case 4: |
| xkeymap_params.variant = optarg; |
| use_xkb = 1; |
| break; |
| case 5: |
| xkeymap_params.options = optarg; |
| use_xkb = 1; |
| break; |
| case 6: |
| xkeymap_params.locale = optarg; |
| use_xkb = 1; |
| break; |
| #endif |
| case 'a': |
| options |= OPT_A; |
| break; |
| case 'b': |
| options |= OPT_B; |
| break; |
| case 'c': |
| flags |= LK_FLAG_CLEAR_COMPOSE; |
| break; |
| case 'C': |
| console = optarg; |
| break; |
| case 'd': |
| options |= OPT_D; |
| break; |
| case 'm': |
| options |= OPT_M; |
| break; |
| case 'p': |
| options |= OPT_P; |
| break; |
| case 's': |
| flags |= LK_FLAG_CLEAR_STRINGS; |
| break; |
| case 'u': |
| options |= OPT_U; |
| flags |= LK_FLAG_UNICODE_MODE; |
| flags |= LK_FLAG_PREFER_UNICODE; |
| break; |
| case 'q': |
| lk_set_log_priority(ctx, LOG_ERR); |
| break; |
| case 't': |
| options |= OPT_T; |
| if (optarg) { |
| table_shape = atoi(optarg); |
| } |
| break; |
| case 'v': |
| lk_set_log_priority(ctx, LOG_INFO); |
| break; |
| case 'V': |
| print_version_and_exit(); |
| break; |
| case 'h': |
| usage(EXIT_SUCCESS, opthelp); |
| break; |
| case '?': |
| usage(EX_USAGE, opthelp); |
| break; |
| } |
| } |
| |
| if ((options & OPT_U) && (options & OPT_A)) |
| kbd_error(EXIT_FAILURE, 0, _("Options %s and %s are mutually exclusive."), |
| "--unicode", "--ascii"); |
| |
| if (!(options & OPT_M) && !(options & OPT_B) && !(options & OPT_T)) { |
| /* get console */ |
| if ((fd = getfd(console)) < 0) |
| kbd_error(EXIT_FAILURE, 0, _("Couldn't get a file descriptor referring to the console.")); |
| |
| /* check whether the keyboard is in Unicode mode */ |
| if (ioctl(fd, KDGKBMODE, &kbd_mode)) |
| kbd_error(EXIT_FAILURE, errno, _("Unable to read keyboard mode")); |
| if (ioctl(fd, KDGETMODE, &kd_mode)) |
| kbd_error(EXIT_FAILURE, errno, _("Unable to read console mode")); |
| |
| if (kbd_mode == K_UNICODE) { |
| if (options & OPT_A) { |
| kbd_warning(0, |
| _("Warning: loading non-Unicode keymap on Unicode console\n" |
| " (perhaps you want to do `kbd_mode -a'?)")); |
| } else { |
| flags |= LK_FLAG_PREFER_UNICODE; |
| } |
| |
| /* reset -u option if keyboard is in K_UNICODE anyway */ |
| flags ^= LK_FLAG_UNICODE_MODE; |
| |
| } else if (options & OPT_U && kd_mode != KD_GRAPHICS) { |
| kbd_warning(0, |
| _("Warning: loading Unicode keymap on non-Unicode console\n" |
| " (perhaps you want to do `kbd_mode -u'?)")); |
| } |
| } |
| |
| lk_set_parser_flags(ctx, flags); |
| |
| dirpath = dirpath1; |
| if ((ev = getenv("LOADKEYS_KEYMAP_PATH")) != NULL) { |
| dirpath2[0] = ev; |
| dirpath = dirpath2; |
| } |
| |
| if (options & OPT_D) { |
| if (!(fp = kbdfile_new(fctx))) |
| kbd_error(EXIT_FAILURE, 0, _("Unable to create kbdfile instance: %m")); |
| |
| /* first read default map - search starts in . */ |
| if (kbdfile_find(DEFMAP, dirpath, suffixes, fp)) |
| kbd_error(EXIT_FAILURE, 0, _("Unable to find file: %s"), DEFMAP); |
| |
| rc = lk_parse_keymap(ctx, fp); |
| kbdfile_free(fp); |
| |
| if (rc == -1) |
| goto fail; |
| |
| #ifdef USE_XKB |
| } else if (use_xkb) { |
| if (!xkeymap_params.locale || !*xkeymap_params.locale) |
| xkeymap_params.locale = getenv("LC_ALL"); |
| if (!xkeymap_params.locale || !*xkeymap_params.locale) |
| xkeymap_params.locale = getenv("LC_CTYPE"); |
| if (!xkeymap_params.locale || !*xkeymap_params.locale) |
| xkeymap_params.locale = getenv("LANG"); |
| if (!xkeymap_params.locale || !*xkeymap_params.locale) |
| xkeymap_params.locale = "C"; |
| |
| rc = convert_xkb_keymap(ctx, &xkeymap_params); |
| if (rc == -1) |
| goto fail; |
| |
| #endif |
| } else if (optind == argc) { |
| if (!(fp = kbdfile_new(fctx))) |
| kbd_error(EXIT_FAILURE, 0, _("Unable to create kbdfile instance: %m")); |
| |
| kbdfile_set_file(fp, stdin); |
| kbdfile_set_pathname(fp, "<stdin>"); |
| |
| rc = lk_parse_keymap(ctx, fp); |
| kbdfile_free(fp); |
| |
| if (rc == -1) |
| goto fail; |
| } |
| |
| for (i = optind; argv[i]; i++) { |
| if (!(fp = kbdfile_new(fctx))) { |
| kbd_error(EXIT_FAILURE, 0, _("Unable to create kbdfile instance: %m")); |
| } |
| |
| if (!strcmp(argv[i], "-")) { |
| kbdfile_set_file(fp, stdin); |
| kbdfile_set_pathname(fp, "<stdin>"); |
| |
| } else if (kbdfile_find(argv[i], dirpath, suffixes, fp)) { |
| kbd_warning(0, _("Unable to open file: %s: %m"), argv[i]); |
| goto fail; |
| } |
| |
| rc = lk_parse_keymap(ctx, fp); |
| kbdfile_free(fp); |
| |
| if (rc == -1) |
| goto fail; |
| } |
| |
| if (!(options & OPT_P)) { |
| if (options & OPT_B) { |
| rc = lk_dump_bkeymap(ctx, stdout); |
| } else if (options & OPT_M) { |
| rc = lk_dump_ctable(ctx, stdout); |
| } else if (options & OPT_T) { |
| rc = lk_dump_keymap2(ctx, stdout, table_shape, 0); |
| if (rc < 0) |
| goto fail; |
| #ifdef KDGKBDIACR |
| lk_dump_diacs(ctx, stdout); |
| #endif |
| rc = 0; |
| } else { |
| rc = lk_load_keymap(ctx, fd, kbd_mode); |
| } |
| } |
| else if (getenv("LK_DUMPKEYS")) { |
| lk_dump_keymap(ctx, stdout, LK_SHAPE_SEPARATE_LINES, 0); |
| lk_dump_diacs(ctx, stdout); |
| } |
| |
| fail: |
| lk_free(ctx); |
| kbdfile_context_free(fctx); |
| |
| if (fd >= 0) |
| close(fd); |
| |
| if (rc < 0) |
| exit(EXIT_FAILURE); |
| |
| exit(EXIT_SUCCESS); |
| } |