tools: avoid use of a private api
[platform/upstream/libxkbcommon.git] / tools / compile-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_RMLVO,
43     FORMAT_KEYMAP,
44     FORMAT_KCCGST,
45     FORMAT_KEYMAP_FROM_XKB,
46 } output_format = FORMAT_KEYMAP;
47 static darray(const char *) includes;
48
49 static void
50 usage(char **argv)
51 {
52     printf("Usage: %s [OPTIONS]\n"
53            "\n"
54            "Compile the given RMLVO to a keymap and print it\n"
55            "\n"
56            "Options:\n"
57            " --verbose\n"
58            "    Enable verbose debugging output\n"
59            " --kccgst\n"
60            "    Print a keymap which only includes the KcCGST component names instead of the full keymap\n"
61            " --rmlvo\n"
62            "    Print the full RMLVO with the defaults filled in for missing elements\n"
63            " --from-xkb\n"
64            "    Load the XKB file from stdin, ignore RMLVO options. This option\n"
65            "    must not be used with --kccgst.\n"
66            " --include\n"
67            "    Add the given path to the include path list. This option is\n"
68            "    order-dependent, include paths given first are searched first.\n"
69            "    If an include path is given, the default include path list is\n"
70            "    not used. Use --include-defaults to add the default include\n"
71            "    paths\n"
72            " --include-defaults\n"
73            "    Add the default set of include directories.\n"
74            "    This option is order-dependent, include paths given first\n"
75            "    are searched first.\n"
76            "\n"
77            "XKB-specific options:\n"
78            " --rules <rules>\n"
79            "    The XKB ruleset (default: '%s')\n"
80            " --model <model>\n"
81            "    The XKB model (default: '%s')\n"
82            " --layout <layout>\n"
83            "    The XKB layout (default: '%s')\n"
84            " --variant <variant>\n"
85            "    The XKB layout variant (default: '%s')\n"
86            " --options <options>\n"
87            "    The XKB options (default: '%s')\n"
88            "\n",
89            argv[0], DEFAULT_XKB_RULES,
90            DEFAULT_XKB_MODEL, DEFAULT_XKB_LAYOUT,
91            DEFAULT_XKB_VARIANT ? DEFAULT_XKB_VARIANT : "<none>",
92            DEFAULT_XKB_OPTIONS ? DEFAULT_XKB_OPTIONS : "<none>");
93 }
94
95 static bool
96 parse_options(int argc, char **argv, struct xkb_rule_names *names)
97 {
98     enum options {
99         OPT_VERBOSE,
100         OPT_KCCGST,
101         OPT_RMLVO,
102         OPT_FROM_XKB,
103         OPT_INCLUDE,
104         OPT_INCLUDE_DEFAULTS,
105         OPT_RULES,
106         OPT_MODEL,
107         OPT_LAYOUT,
108         OPT_VARIANT,
109         OPT_OPTION,
110     };
111     static struct option opts[] = {
112         {"help",             no_argument,            0, 'h'},
113         {"verbose",          no_argument,            0, OPT_VERBOSE},
114         {"kccgst",           no_argument,            0, OPT_KCCGST},
115         {"rmlvo",            no_argument,            0, OPT_RMLVO},
116         {"from-xkb",         no_argument,            0, OPT_FROM_XKB},
117         {"include",          required_argument,      0, OPT_INCLUDE},
118         {"include-defaults", no_argument,            0, OPT_INCLUDE_DEFAULTS},
119         {"rules",            required_argument,      0, OPT_RULES},
120         {"model",            required_argument,      0, OPT_MODEL},
121         {"layout",           required_argument,      0, OPT_LAYOUT},
122         {"variant",          required_argument,      0, OPT_VARIANT},
123         {"options",          required_argument,      0, OPT_OPTION},
124         {0, 0, 0, 0},
125     };
126
127     while (1) {
128         int c;
129         int option_index = 0;
130         c = getopt_long(argc, argv, "h", opts, &option_index);
131         if (c == -1)
132             break;
133
134         switch (c) {
135         case 'h':
136             usage(argv);
137             exit(0);
138         case OPT_VERBOSE:
139             verbose = true;
140             break;
141         case OPT_KCCGST:
142             output_format = FORMAT_KCCGST;
143             break;
144         case OPT_RMLVO:
145             output_format = FORMAT_RMLVO;
146             break;
147         case OPT_FROM_XKB:
148             output_format = FORMAT_KEYMAP_FROM_XKB;
149             break;
150         case OPT_INCLUDE:
151             darray_append(includes, optarg);
152             break;
153         case OPT_INCLUDE_DEFAULTS:
154             darray_append(includes, DEFAULT_INCLUDE_PATH_PLACEHOLDER);
155             break;
156         case OPT_RULES:
157             names->rules = optarg;
158             break;
159         case OPT_MODEL:
160             names->model = optarg;
161             break;
162         case OPT_LAYOUT:
163             names->layout = optarg;
164             break;
165         case OPT_VARIANT:
166             names->variant = optarg;
167             break;
168         case OPT_OPTION:
169             names->options = optarg;
170             break;
171         default:
172             usage(argv);
173             exit(EXIT_INVALID_USAGE);
174         }
175
176     }
177
178     return true;
179 }
180
181 static bool
182 print_rmlvo(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo)
183 {
184     printf("rules: \"%s\"\nmodel: \"%s\"\nlayout: \"%s\"\nvariant: \"%s\"\noptions: \"%s\"\n",
185            rmlvo->rules, rmlvo->model, rmlvo->layout,
186            rmlvo->variant ? rmlvo->variant : "",
187            rmlvo->options ? rmlvo->options : "");
188     return true;
189 }
190
191 static bool
192 print_kccgst(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo)
193 {
194         struct xkb_component_names kccgst;
195
196         if (!xkb_components_from_rules(ctx, rmlvo, &kccgst))
197             return false;
198
199         printf("xkb_keymap {\n"
200                "  xkb_keycodes { include \"%s\" };\n"
201                "  xkb_types { include \"%s\" };\n"
202                "  xkb_compat { include \"%s\" };\n"
203                "  xkb_symbols { include \"%s\" };\n"
204                "};\n",
205                kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols);
206         free(kccgst.keycodes);
207         free(kccgst.types);
208         free(kccgst.compat);
209         free(kccgst.symbols);
210
211         return true;
212 }
213
214 static bool
215 print_keymap(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo)
216 {
217     struct xkb_keymap *keymap;
218
219     keymap = xkb_keymap_new_from_names(ctx, rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
220     if (keymap == NULL)
221         return false;
222
223     printf("%s\n", xkb_keymap_get_as_string(keymap,
224                                             XKB_KEYMAP_FORMAT_TEXT_V1));
225     xkb_keymap_unref(keymap);
226     return true;
227 }
228
229 static bool
230 print_keymap_from_file(struct xkb_context *ctx)
231 {
232     struct xkb_keymap *keymap = NULL;
233     char *keymap_string = NULL;
234     FILE *file = NULL;
235     bool success = false;
236
237     file = tmpfile();
238     if (!file) {
239         fprintf(stderr, "Failed to create tmpfile\n");
240         goto out;
241     }
242
243     while (true) {
244         char buf[4096];
245         size_t len;
246
247         len = fread(buf, 1, sizeof(buf), stdin);
248         if (ferror(stdin)) {
249             fprintf(stderr, "Failed to read from stdin\n");
250             goto out;
251         }
252         if (len > 0) {
253             size_t wlen = fwrite(buf, 1, len, file);
254             if (wlen != len) {
255                 fprintf(stderr, "Failed to write to tmpfile\n");
256                 goto out;
257             }
258         }
259         if (feof(stdin))
260             break;
261     }
262     fseek(file, 0, SEEK_SET);
263     keymap = xkb_keymap_new_from_file(ctx, file,
264                                       XKB_KEYMAP_FORMAT_TEXT_V1, 0);
265     if (!keymap) {
266         fprintf(stderr, "Couldn't create xkb keymap\n");
267         goto out;
268     }
269
270     keymap_string = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
271     if (!keymap_string) {
272         fprintf(stderr, "Couldn't get the keymap string\n");
273         goto out;
274     }
275
276     fputs(keymap_string, stdout);
277     success = true;
278
279 out:
280     if (file)
281         fclose(file);
282     xkb_keymap_unref(keymap);
283     free(keymap_string);
284
285     return success;
286 }
287
288 int
289 main(int argc, char **argv)
290 {
291     struct xkb_context *ctx;
292     struct xkb_rule_names names = {
293         .rules = DEFAULT_XKB_RULES,
294         .model = DEFAULT_XKB_MODEL,
295         /* layout and variant are tied together, so we either get user-supplied for
296          * both or default for both, see below */
297         .layout = NULL,
298         .variant = NULL,
299         .options = DEFAULT_XKB_OPTIONS,
300     };
301     int rc = 1;
302     const char **path;
303
304     if (argc <= 1) {
305         usage(argv);
306         return EXIT_INVALID_USAGE;
307     }
308
309     if (!parse_options(argc, argv, &names))
310         return EXIT_INVALID_USAGE;
311
312     /* Now fill in the layout */
313     if (isempty(names.layout)) {
314         if (!isempty(names.variant)) {
315             fprintf(stderr, "Error: a variant requires a layout\n");
316             return EXIT_INVALID_USAGE;
317         }
318         names.layout = DEFAULT_XKB_LAYOUT;
319         names.variant = DEFAULT_XKB_VARIANT;
320     }
321
322     ctx = xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES);
323     assert(ctx);
324
325     if (verbose) {
326         xkb_context_set_log_level(ctx, XKB_LOG_LEVEL_DEBUG);
327         xkb_context_set_log_verbosity(ctx, 10);
328     }
329
330     if (darray_empty(includes))
331         darray_append(includes, DEFAULT_INCLUDE_PATH_PLACEHOLDER);
332
333     darray_foreach(path, includes) {
334         if (streq(*path, DEFAULT_INCLUDE_PATH_PLACEHOLDER))
335             xkb_context_include_path_append_default(ctx);
336         else
337             xkb_context_include_path_append(ctx, *path);
338     }
339
340     if (output_format == FORMAT_RMLVO) {
341         rc = print_rmlvo(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE;
342     } else if (output_format == FORMAT_KEYMAP) {
343         rc = print_keymap(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE;
344     } else if (output_format == FORMAT_KCCGST) {
345         rc = print_kccgst(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE;
346     } else if (output_format == FORMAT_KEYMAP_FROM_XKB) {
347         rc = print_keymap_from_file(ctx);
348     }
349
350     xkb_context_unref(ctx);
351
352     return rc;
353 }