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