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