/* * Copyright © 2018 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "config.h" #include #include #include #include #include #include #include #include "xkbcommon/xkbcommon.h" #if ENABLE_PRIVATE_APIS #include "xkbcomp/xkbcomp-priv.h" #include "xkbcomp/rules.h" #endif #include "tools-common.h" #define DEFAULT_INCLUDE_PATH_PLACEHOLDER "__defaults__" static bool verbose = false; static enum output_format { FORMAT_RMLVO, FORMAT_KEYMAP, FORMAT_KCCGST, FORMAT_KEYMAP_FROM_XKB, } output_format = FORMAT_KEYMAP; static const char *includes[64]; static size_t num_includes = 0; static void usage(char **argv) { printf("Usage: %s [OPTIONS]\n" "\n" "Compile the given RMLVO to a keymap and print it\n" "\n" "Options:\n" " --verbose\n" " Enable verbose debugging output\n" #if ENABLE_PRIVATE_APIS " --kccgst\n" " Print a keymap which only includes the KcCGST component names instead of the full keymap\n" #endif " --rmlvo\n" " Print the full RMLVO with the defaults filled in for missing elements\n" " --from-xkb\n" " Load the XKB file from stdin, ignore RMLVO options.\n" #if ENABLE_PRIVATE_APIS " This option must not be used with --kccgst.\n" #endif " --include\n" " Add the given path to the include path list. This option is\n" " order-dependent, include paths given first are searched first.\n" " If an include path is given, the default include path list is\n" " not used. Use --include-defaults to add the default include\n" " paths\n" " --include-defaults\n" " Add the default set of include directories.\n" " This option is order-dependent, include paths given first\n" " are searched first.\n" "\n" "XKB-specific options:\n" " --rules \n" " The XKB ruleset (default: '%s')\n" " --model \n" " The XKB model (default: '%s')\n" " --layout \n" " The XKB layout (default: '%s')\n" " --variant \n" " The XKB layout variant (default: '%s')\n" " --options \n" " The XKB options (default: '%s')\n" "\n", argv[0], DEFAULT_XKB_RULES, DEFAULT_XKB_MODEL, DEFAULT_XKB_LAYOUT, DEFAULT_XKB_VARIANT ? DEFAULT_XKB_VARIANT : "", DEFAULT_XKB_OPTIONS ? DEFAULT_XKB_OPTIONS : ""); } static bool parse_options(int argc, char **argv, struct xkb_rule_names *names) { enum options { OPT_VERBOSE, OPT_KCCGST, OPT_RMLVO, OPT_FROM_XKB, OPT_INCLUDE, OPT_INCLUDE_DEFAULTS, OPT_RULES, OPT_MODEL, OPT_LAYOUT, OPT_VARIANT, OPT_OPTION, }; static struct option opts[] = { {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, OPT_VERBOSE}, #if ENABLE_PRIVATE_APIS {"kccgst", no_argument, 0, OPT_KCCGST}, #endif {"rmlvo", no_argument, 0, OPT_RMLVO}, {"from-xkb", no_argument, 0, OPT_FROM_XKB}, {"include", required_argument, 0, OPT_INCLUDE}, {"include-defaults", no_argument, 0, OPT_INCLUDE_DEFAULTS}, {"rules", required_argument, 0, OPT_RULES}, {"model", required_argument, 0, OPT_MODEL}, {"layout", required_argument, 0, OPT_LAYOUT}, {"variant", required_argument, 0, OPT_VARIANT}, {"options", required_argument, 0, OPT_OPTION}, {0, 0, 0, 0}, }; while (1) { int c; int option_index = 0; c = getopt_long(argc, argv, "h", opts, &option_index); if (c == -1) break; switch (c) { case 'h': usage(argv); exit(0); case OPT_VERBOSE: verbose = true; break; case OPT_KCCGST: output_format = FORMAT_KCCGST; break; case OPT_RMLVO: output_format = FORMAT_RMLVO; break; case OPT_FROM_XKB: output_format = FORMAT_KEYMAP_FROM_XKB; break; case OPT_INCLUDE: if (num_includes >= ARRAY_SIZE(includes)) { fprintf(stderr, "error: too many includes\n"); exit(EXIT_INVALID_USAGE); } includes[num_includes++] = optarg; break; case OPT_INCLUDE_DEFAULTS: if (num_includes >= ARRAY_SIZE(includes)) { fprintf(stderr, "error: too many includes\n"); exit(EXIT_INVALID_USAGE); } includes[num_includes++] = DEFAULT_INCLUDE_PATH_PLACEHOLDER; break; case OPT_RULES: names->rules = optarg; break; case OPT_MODEL: names->model = optarg; break; case OPT_LAYOUT: names->layout = optarg; break; case OPT_VARIANT: names->variant = optarg; break; case OPT_OPTION: names->options = optarg; break; default: usage(argv); exit(EXIT_INVALID_USAGE); } } return true; } static bool print_rmlvo(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo) { printf("rules: \"%s\"\nmodel: \"%s\"\nlayout: \"%s\"\nvariant: \"%s\"\noptions: \"%s\"\n", rmlvo->rules, rmlvo->model, rmlvo->layout, rmlvo->variant ? rmlvo->variant : "", rmlvo->options ? rmlvo->options : ""); return true; } static bool print_kccgst(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo) { #if ENABLE_PRIVATE_APIS struct xkb_component_names kccgst; if (!xkb_components_from_rules(ctx, rmlvo, &kccgst)) return false; printf("xkb_keymap {\n" " xkb_keycodes { include \"%s\" };\n" " xkb_types { include \"%s\" };\n" " xkb_compat { include \"%s\" };\n" " xkb_symbols { include \"%s\" };\n" "};\n", kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols); free(kccgst.keycodes); free(kccgst.types); free(kccgst.compat); free(kccgst.symbols); return true; #else return false; #endif } static bool print_keymap(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo) { struct xkb_keymap *keymap; keymap = xkb_keymap_new_from_names(ctx, rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); if (keymap == NULL) return false; printf("%s\n", xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1)); xkb_keymap_unref(keymap); return true; } static bool print_keymap_from_file(struct xkb_context *ctx) { struct xkb_keymap *keymap = NULL; char *keymap_string = NULL; FILE *file = NULL; bool success = false; file = tmpfile(); if (!file) { fprintf(stderr, "Failed to create tmpfile\n"); goto out; } while (true) { char buf[4096]; size_t len; len = fread(buf, 1, sizeof(buf), stdin); if (ferror(stdin)) { fprintf(stderr, "Failed to read from stdin\n"); goto out; } if (len > 0) { size_t wlen = fwrite(buf, 1, len, file); if (wlen != len) { fprintf(stderr, "Failed to write to tmpfile\n"); goto out; } } if (feof(stdin)) break; } fseek(file, 0, SEEK_SET); keymap = xkb_keymap_new_from_file(ctx, file, XKB_KEYMAP_FORMAT_TEXT_V1, 0); if (!keymap) { fprintf(stderr, "Couldn't create xkb keymap\n"); goto out; } keymap_string = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); if (!keymap_string) { fprintf(stderr, "Couldn't get the keymap string\n"); goto out; } fputs(keymap_string, stdout); success = true; out: if (file) fclose(file); xkb_keymap_unref(keymap); free(keymap_string); return success; } int main(int argc, char **argv) { struct xkb_context *ctx; struct xkb_rule_names names = { .rules = DEFAULT_XKB_RULES, .model = DEFAULT_XKB_MODEL, /* layout and variant are tied together, so we either get user-supplied for * both or default for both, see below */ .layout = NULL, .variant = NULL, .options = DEFAULT_XKB_OPTIONS, }; int rc = 1; if (argc <= 1) { usage(argv); return EXIT_INVALID_USAGE; } if (!parse_options(argc, argv, &names)) return EXIT_INVALID_USAGE; /* Now fill in the layout */ if (!names.layout || !*names.layout) { if (names.variant && *names.variant) { fprintf(stderr, "Error: a variant requires a layout\n"); return EXIT_INVALID_USAGE; } names.layout = DEFAULT_XKB_LAYOUT; names.variant = DEFAULT_XKB_VARIANT; } ctx = xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES); assert(ctx); if (verbose) { xkb_context_set_log_level(ctx, XKB_LOG_LEVEL_DEBUG); xkb_context_set_log_verbosity(ctx, 10); } if (num_includes == 0) includes[num_includes++] = DEFAULT_INCLUDE_PATH_PLACEHOLDER; for (size_t i = 0; i < num_includes; i++) { const char *include = includes[i]; if (strcmp(include, DEFAULT_INCLUDE_PATH_PLACEHOLDER) == 0) xkb_context_include_path_append_default(ctx); else xkb_context_include_path_append(ctx, include); } if (output_format == FORMAT_RMLVO) { rc = print_rmlvo(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE; } else if (output_format == FORMAT_KEYMAP) { rc = print_keymap(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE; } else if (output_format == FORMAT_KCCGST) { rc = print_kccgst(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE; } else if (output_format == FORMAT_KEYMAP_FROM_XKB) { rc = print_keymap_from_file(ctx); } xkb_context_unref(ctx); return rc; }