tools: add ability to compile from kccgst to rmlvo-to-keymap
[platform/upstream/libxkbcommon.git] / tools / rmlvo-to-keymap.c
1 /*
2  * Copyright © 2018 Red Hat, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23
24 #include "config.h"
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <getopt.h>
29 #include <stdio.h>
30 #include <stdbool.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "xkbcomp/xkbcomp-priv.h"
35 #include "xkbcomp/rules.h"
36 #include "xkbcommon/xkbcommon.h"
37
38 #define DEFAULT_INCLUDE_PATH_PLACEHOLDER "__defaults__"
39
40 static bool verbose = false;
41 static enum output_format {
42     FORMAT_KEYMAP,
43     FORMAT_KCCGST,
44     FORMAT_KEYMAP_FROM_XKB,
45 } output_format = FORMAT_KEYMAP;
46 static darray(const char *) includes;
47
48 static void
49 usage(char **argv)
50 {
51     printf("Usage: %s [OPTIONS]\n"
52            "\n"
53            "Compile the given RMLVO to a keymap and print it\n"
54            "\n"
55            "Options:\n"
56            " --verbose\n"
57            "    Enable verbose debugging output\n"
58            " --kccgst\n"
59            "    Print a keymap which only includes the KcCGST component names instead of the full keymap\n"
60            " --from-xkb\n"
61            "    Load the XKB file from stdin, ignore RMLVO options. This option\n"
62            "    must not be used with --kccgst.\n"
63            " --include\n"
64            "    Add the given path to the include path list. This option is\n"
65            "    order-dependent, include paths given first are searched first.\n"
66            "    If an include path is given, the default include path list is\n"
67            "    not used. Use --include-defaults to add the default include\n"
68            "    paths\n"
69            " --include-defaults\n"
70            "    Add the default set of include directories.\n"
71            "    This option is order-dependent, include paths given first\n"
72            "    are searched first.\n"
73            "\n"
74            "XKB-specific options:\n"
75            " --rules <rules>\n"
76            "    The XKB ruleset (default: '%s')\n"
77            " --model <model>\n"
78            "    The XKB model (default: '%s')\n"
79            " --layout <layout>\n"
80            "    The XKB layout (default: '%s')\n"
81            " --variant <variant>\n"
82            "    The XKB layout variant (default: '%s')\n"
83            " --options <options>\n"
84            "    The XKB options (default: '%s')\n"
85            "\n",
86            argv[0], DEFAULT_XKB_RULES,
87            DEFAULT_XKB_MODEL, DEFAULT_XKB_LAYOUT,
88            DEFAULT_XKB_VARIANT ? DEFAULT_XKB_VARIANT : "<none>",
89            DEFAULT_XKB_OPTIONS ? DEFAULT_XKB_OPTIONS : "<none>");
90 }
91
92 static bool
93 parse_options(int argc, char **argv, struct xkb_rule_names *names)
94 {
95     enum options {
96         OPT_VERBOSE,
97         OPT_KCCGST,
98         OPT_FROM_XKB,
99         OPT_INCLUDE,
100         OPT_INCLUDE_DEFAULTS,
101         OPT_RULES,
102         OPT_MODEL,
103         OPT_LAYOUT,
104         OPT_VARIANT,
105         OPT_OPTION,
106     };
107     static struct option opts[] = {
108         {"help",             no_argument,            0, 'h'},
109         {"verbose",          no_argument,            0, OPT_VERBOSE},
110         {"kccgst",           no_argument,            0, OPT_KCCGST},
111         {"from-xkb",         no_argument,            0, OPT_FROM_XKB},
112         {"include",          required_argument,      0, OPT_INCLUDE},
113         {"include-defaults", no_argument,            0, OPT_INCLUDE_DEFAULTS},
114         {"rules",            required_argument,      0, OPT_RULES},
115         {"model",            required_argument,      0, OPT_MODEL},
116         {"layout",           required_argument,      0, OPT_LAYOUT},
117         {"variant",          required_argument,      0, OPT_VARIANT},
118         {"options",          required_argument,      0, OPT_OPTION},
119         {0, 0, 0, 0},
120     };
121
122     while (1) {
123         int c;
124         int option_index = 0;
125         c = getopt_long(argc, argv, "h", opts, &option_index);
126         if (c == -1)
127             break;
128
129         switch (c) {
130         case 'h':
131             usage(argv);
132             exit(0);
133         case OPT_VERBOSE:
134             verbose = true;
135             break;
136         case OPT_KCCGST:
137             output_format = FORMAT_KCCGST;
138             break;
139         case OPT_FROM_XKB:
140             output_format = FORMAT_KEYMAP_FROM_XKB;
141             break;
142         case OPT_INCLUDE:
143             darray_append(includes, optarg);
144             break;
145         case OPT_INCLUDE_DEFAULTS:
146             darray_append(includes, DEFAULT_INCLUDE_PATH_PLACEHOLDER);
147             break;
148         case OPT_RULES:
149             names->rules = optarg;
150             break;
151         case OPT_MODEL:
152             names->model = optarg;
153             break;
154         case OPT_LAYOUT:
155             names->layout = optarg;
156             break;
157         case OPT_VARIANT:
158             names->variant = optarg;
159             break;
160         case OPT_OPTION:
161             names->options = optarg;
162             break;
163         default:
164             usage(argv);
165             exit(1);
166         }
167
168     }
169
170     return true;
171 }
172
173 static bool
174 print_kccgst(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo)
175 {
176         struct xkb_component_names kccgst;
177
178         if (!xkb_components_from_rules(ctx, rmlvo, &kccgst))
179             return false;
180
181         printf("xkb_keymap {\n"
182                "  xkb_keycodes { include \"%s\" };\n"
183                "  xkb_types { include \"%s\" };\n"
184                "  xkb_compat { include \"%s\" };\n"
185                "  xkb_symbols { include \"%s\" };\n"
186                "};\n",
187                kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols);
188         free(kccgst.keycodes);
189         free(kccgst.types);
190         free(kccgst.compat);
191         free(kccgst.symbols);
192
193         return true;
194 }
195
196 static bool
197 print_keymap(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo)
198 {
199     struct xkb_keymap *keymap;
200
201     keymap = xkb_keymap_new_from_names(ctx, rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
202     if (keymap == NULL)
203         return false;
204
205     printf("%s\n", xkb_keymap_get_as_string(keymap,
206                                             XKB_KEYMAP_FORMAT_TEXT_V1));
207     xkb_keymap_unref(keymap);
208     return true;
209 }
210
211 static bool
212 print_keymap_from_file(struct xkb_context *ctx)
213 {
214     struct xkb_keymap *keymap = NULL;
215     char *keymap_string = NULL;
216     FILE *file = NULL;
217     bool success = false;
218
219     file = tmpfile();
220     if (!file) {
221         fprintf(stderr, "Failed to create tmpfile\n");
222         goto out;
223     }
224
225     while (true) {
226         char buf[4096];
227         size_t len;
228
229         len = fread(buf, 1, sizeof(buf), stdin);
230         if (ferror(stdin)) {
231             fprintf(stderr, "Failed to read from stdin\n");
232             goto out;
233         }
234         if (len > 0) {
235             size_t wlen = fwrite(buf, 1, len, file);
236             if (wlen != len) {
237                 fprintf(stderr, "Failed to write to tmpfile\n");
238                 goto out;
239             }
240         }
241         if (feof(stdin))
242             break;
243     }
244     fseek(file, 0, SEEK_SET);
245     keymap = xkb_keymap_new_from_file(ctx, file,
246                                       XKB_KEYMAP_FORMAT_TEXT_V1, 0);
247     if (!keymap) {
248         fprintf(stderr, "Couldn't create xkb keymap\n");
249         goto out;
250     }
251
252     keymap_string = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
253     if (!keymap_string) {
254         fprintf(stderr, "Couldn't get the keymap string\n");
255         goto out;
256     }
257
258     fputs(keymap_string, stdout);
259     success = true;
260
261 out:
262     if (file)
263         fclose(file);
264     xkb_keymap_unref(keymap);
265     free(keymap_string);
266
267     return success;
268 }
269
270 int
271 main(int argc, char **argv)
272 {
273     struct xkb_context *ctx;
274     struct xkb_rule_names names = {
275         .rules = NULL,
276         .model = NULL,
277         .layout = NULL,
278         .variant = NULL,
279         .options = NULL,
280     };
281     int rc = 1;
282     const char **path;
283
284     if (argc <= 1) {
285         usage(argv);
286         return 1;
287     }
288
289     if (!parse_options(argc, argv, &names))
290         return 1;
291
292     ctx = xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES);
293     assert(ctx);
294
295     if (verbose) {
296         xkb_context_set_log_level(ctx, XKB_LOG_LEVEL_DEBUG);
297         xkb_context_set_log_verbosity(ctx, 10);
298     }
299
300     xkb_context_sanitize_rule_names(ctx, &names);
301     if (darray_empty(includes))
302         darray_append(includes, DEFAULT_INCLUDE_PATH_PLACEHOLDER);
303
304     darray_foreach(path, includes) {
305         if (streq(*path, DEFAULT_INCLUDE_PATH_PLACEHOLDER))
306             xkb_context_include_path_append_default(ctx);
307         else
308             xkb_context_include_path_append(ctx, *path);
309     }
310
311     if (output_format == FORMAT_KEYMAP) {
312         rc = print_keymap(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE;
313     } else if (output_format == FORMAT_KCCGST) {
314         rc = print_kccgst(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE;
315     } else if (output_format == FORMAT_KEYMAP_FROM_XKB) {
316         rc = print_keymap_from_file(ctx);
317     }
318
319     xkb_context_unref(ctx);
320
321     return rc;
322 }