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