xcursor-pro: init at 2.0.2 (#335188)
[NixPkgs.git] / pkgs / by-name / xk / xkbvalidate / xkbvalidate.c
blobd485ff604ca0b469ebfa31ff8fe59599bead2ca6
1 #include <stdarg.h>
2 #include <stdbool.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
7 #include <xkbcommon/xkbcommon.h>
9 static char **log_buffer = NULL;
10 static int log_buffer_size = 0;
11 static bool log_alloc_success = true;
13 static void add_log(struct xkb_context *ctx, enum xkb_log_level level,
14 const char *fmt, va_list args)
16 size_t buflen;
17 va_list tmpargs;
19 log_buffer_size++;
21 if (log_buffer == NULL)
22 log_buffer = malloc(sizeof(char *));
23 else
24 log_buffer = realloc(log_buffer, sizeof(char *) * log_buffer_size);
26 if (log_buffer == NULL) {
27 perror("buffer alloc");
28 log_alloc_success = false;
29 log_buffer_size--;
30 return;
33 /* Unfortunately, vasprintf() is a GNU extension and thus not very
34 * portable, so let's first get the required buffer size using a dummy
35 * vsnprintf and afterwards allocate the returned amount of bytes.
37 * We also need to make a copy of the args, because the value of the args
38 * will be indeterminate after the return.
40 va_copy(tmpargs, args);
41 buflen = vsnprintf(NULL, 0, fmt, tmpargs);
42 va_end(tmpargs);
44 log_buffer[log_buffer_size - 1] = malloc(++buflen);
46 if (vsnprintf(log_buffer[log_buffer_size - 1], buflen, fmt, args) == -1) {
47 perror("log line alloc");
48 log_alloc_success = false;
50 va_end(args);
53 static void print_logs(void)
55 for (int i = 0; i < log_buffer_size; ++i)
56 fprintf(stderr, " %s", log_buffer[i]);
59 static void free_logs(void)
61 if (log_buffer == NULL)
62 return;
63 for (int i = 0; i < log_buffer_size; ++i)
64 free(log_buffer[i]);
65 free(log_buffer);
66 log_buffer = NULL;
67 log_buffer_size = 0;
70 static bool try_keymap(struct xkb_context *ctx, struct xkb_rule_names *rdef)
72 struct xkb_keymap *keymap;
73 bool result = true;
75 if ((keymap = xkb_keymap_new_from_names(ctx, rdef, 0)) == NULL)
76 result = false;
77 else
78 xkb_keymap_unref(keymap);
80 return result;
83 static void print_error(const char *name, const char *value,
84 const char *nixos_option)
86 fprintf(stderr, "\nThe value `%s' for keyboard %s is invalid.\n\n"
87 "Please check the definition in `services.xserver.%s'.\n",
88 value, name, nixos_option);
89 fputs("\nDetailed XKB compiler errors:\n\n", stderr);
90 print_logs();
91 putc('\n', stderr);
94 #define TRY_KEYMAP(name, value, nixos_option) \
95 *rdef = (struct xkb_rule_names) {0}; \
96 free_logs(); \
97 rdef->name = value; \
98 result = try_keymap(ctx, rdef); \
99 if (!log_alloc_success) \
100 goto out; \
101 if (!result) { \
102 print_error(#name, value, nixos_option); \
103 exit_code = EXIT_FAILURE; \
104 goto out; \
107 int main(int argc, char **argv)
109 int exit_code = EXIT_SUCCESS;
110 bool result;
111 struct xkb_context *ctx;
112 struct xkb_rule_names *rdef;
114 if (argc != 5) {
115 fprintf(stderr, "Usage: %s model layout variant options\n", argv[0]);
116 return EXIT_FAILURE;
119 ctx = xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
120 xkb_context_set_log_fn(ctx, add_log);
122 rdef = malloc(sizeof(struct xkb_rule_names));
124 TRY_KEYMAP(model, argv[1], "xkb.model");
125 TRY_KEYMAP(layout, argv[2], "xkb.layout");
126 TRY_KEYMAP(variant, argv[3], "xkb.variant");
127 TRY_KEYMAP(options, argv[4], "xkb.options");
129 free_logs();
130 rdef->model = argv[1];
131 rdef->layout = argv[2];
132 rdef->variant = argv[3];
133 rdef->options = argv[4];
135 result = try_keymap(ctx, rdef);
136 if (!log_alloc_success)
137 goto out;
139 if (!result) {
140 fputs("The XKB keyboard definition failed to compile:\n", stderr);
141 print_logs();
142 exit_code = EXIT_FAILURE;
145 out:
146 free_logs();
147 free(rdef);
148 xkb_context_unref(ctx);
149 return exit_code;