1 /* SPDX-License-Identifier: GPL-2.0 */
2
3 // Read a toml keymap or plain text keymap (ir-keytable 1.14 or earlier
4 // format).
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <limits.h>
11 #include <stdbool.h>
12 #include <sys/types.h>
13 #include <argp.h>
14
15 #include "keymap.h"
16 #include "toml.h"
17
18 #ifdef ENABLE_NLS
19 # define _(string) gettext(string)
20 # include <stdio.h>
21 # include <locale.h>
22 # include <langinfo.h>
23 # include <iconv.h>
24 #else
25 # define _(string) string
26 #endif
27
free_keymap(struct keymap * map)28 void free_keymap(struct keymap *map)
29 {
30 struct scancode_entry *se;
31 struct raw_entry *re;
32 struct protocol_param *param;
33 struct keymap *next;
34
35 while (map) {
36 re = map->raw;
37
38 while (re) {
39 struct raw_entry *next = re->next;
40 free(re->keycode);
41 free(re);
42 re = next;
43 }
44
45 se = map->scancode;
46
47 while (se) {
48 struct scancode_entry *next = se->next;
49 free(se->keycode);
50 free(se);
51 se = next;
52 }
53
54 param = map->param;
55
56 while (param) {
57 struct protocol_param *next = param->next;
58 free(param->name);
59 free(param);
60 param = next;
61 }
62
63 next = map->next;
64
65 free(map->name);
66 free(map->protocol);
67 free(map->variant);
68 free(map);
69
70 map = next;
71 }
72 }
73
parse_plain_keymap(char * fname,struct keymap ** keymap,bool verbose)74 static int parse_plain_keymap(char *fname, struct keymap **keymap, bool verbose)
75 {
76 FILE *fin;
77 int line = 0;
78 char *scancode, *keycode, s[2048];
79 struct scancode_entry *se;
80 struct keymap *map;
81
82 map = calloc(1, sizeof(*map));
83 if (!map) {
84 perror("parse_keymap");
85 return ENOMEM;
86 }
87
88 if (verbose)
89 fprintf(stderr, _("Parsing %s keycode file as plain text\n"), fname);
90
91 fin = fopen(fname, "r");
92 if (!fin) {
93 fprintf(stderr, _("%s: error: cannot open: %m\n"), fname);
94 return EINVAL;
95 }
96
97 while (fgets(s, sizeof(s), fin)) {
98 char *p = s;
99
100 line++;
101 while (*p == ' ' || *p == '\t')
102 p++;
103 if (line==1 && p[0] == '#') {
104 p++;
105 p = strtok(p, "\n\t =:");
106 do {
107 if (!p)
108 goto err_einval;
109 if (!strcmp(p, "table")) {
110 p = strtok(NULL,"\n, ");
111 if (!p)
112 goto err_einval;
113 map->name = strdup(p);
114 } else if (!strcmp(p, "type")) {
115 p = strtok(NULL, " ,\n");
116 if (!p)
117 goto err_einval;
118
119 while (p) {
120 if (!map->protocol) {
121 map->protocol = strdup(p);
122 } else {
123 struct keymap *k;
124
125 k = calloc(1, sizeof(*k));
126 k->protocol = strdup(p);
127 k->next = map->next;
128 map->next = k;
129 }
130
131 p = strtok(NULL, " ,\n");
132 }
133 } else {
134 goto err_einval;
135 }
136 p = strtok(NULL, "\n\t =:");
137 } while (p);
138 continue;
139 }
140
141 if (*p == '\n' || *p == '#')
142 continue;
143
144 scancode = strtok(p, "\n\t =:");
145 if (!scancode)
146 goto err_einval;
147 if (!strcasecmp(scancode, "scancode")) {
148 scancode = strtok(NULL, "\n\t =:");
149 if (!scancode)
150 goto err_einval;
151 }
152
153 keycode = strtok(NULL, "\n\t =:(");
154 if (!keycode)
155 goto err_einval;
156
157 se = calloc(1, sizeof(*se));
158 if (!se) {
159 free_keymap(map);
160 perror("parse_keymap");
161 fclose(fin);
162 return ENOMEM;
163 }
164
165 se->scancode = strtoull(scancode, NULL, 0);
166 se->keycode = strdup(keycode);
167 se->next = map->scancode;
168 map->scancode = se;
169 }
170 fclose(fin);
171
172 if (!map->protocol) {
173 fprintf(stderr, _("Missing protocol in %s\n"), fname);
174 return EINVAL;
175 }
176
177 *keymap = map;
178
179 return 0;
180
181 err_einval:
182 free_keymap(map);
183 fprintf(stderr, _("Invalid parameter on line %d of %s\n"),
184 line, fname);
185 return EINVAL;
186 }
187
parse_rawir_string(const char * fname,char * str,struct raw_entry ** entry)188 static int parse_rawir_string(const char *fname, char *str, struct raw_entry **entry)
189 {
190 struct raw_entry *re;
191 const char sep[] = "\n\r\t\v ,";
192 const char *p;
193 char *copy;
194 int i, size = 0;
195
196 // First do one scan so that we know the length
197 copy = strdup(str);
198 p = strtok(copy, sep);
199 while (p) {
200 size++;
201 p = strtok(NULL, sep);
202 }
203
204 re = calloc(1, sizeof(*re) + sizeof(re->raw[0]) * size);
205 if (!re) {
206 fprintf(stderr, _("Failed to allocate memory"));
207 free(copy);
208 return EINVAL;
209 }
210
211 // Second scan to extract values and validate
212 strcpy(copy, str);
213 p = strtok(copy, sep);
214 i = 0;
215 while (p) {
216 long int value;
217 char *q;
218
219 value = strtoll(p, &q, 10);
220 if (*q || value == 0 || errno) {
221 fprintf(stderr, _("%s: incorrect raw value `%s'"),
222 fname, p);
223 free(copy);
224 free(re);
225 return EINVAL;
226 }
227
228 if (value < 0) {
229 if (i % 2)
230 value = -value;
231 else {
232 fprintf(stderr, _("%s: negative raw value `%ld` at position %d only allowed for gaps/spaces"),
233 fname, value, i);
234 free(copy);
235 free(re);
236 return EINVAL;
237 }
238 }
239
240 if (value <= 0 || value > USHRT_MAX) {
241 fprintf(stderr, _("%s: raw value %ld out of range"),
242 fname, value);
243 free(copy);
244 free(re);
245 return EINVAL;
246 }
247
248 re->raw[i++] = value;
249
250 p = strtok(NULL, sep);
251 }
252
253 free(copy);
254 re->raw_length = size;
255
256 *entry = re;
257
258 return 0;
259 }
260
parse_toml_raw_part(const char * fname,struct toml_array_t * raw,struct keymap * map,bool verbose)261 static int parse_toml_raw_part(const char *fname, struct toml_array_t *raw, struct keymap *map, bool verbose)
262 {
263 char *keycode, *raw_str;
264 struct toml_table_t *t;
265 struct raw_entry *re;
266 const char *traw;
267 int ind = 0;
268
269 while ((t = toml_table_at(raw, ind++)) != NULL) {
270 traw = toml_raw_in(t, "keycode");
271 if (!traw) {
272 fprintf(stderr, _("%s: invalid keycode for raw entry %d\n"),
273 fname, ind);
274 return EINVAL;
275 }
276
277 if (toml_rtos(traw, &keycode)) {
278 fprintf(stderr, _("%s: bad value `%s' for keycode\n"),
279 fname, traw);
280 return EINVAL;
281 }
282
283 traw = toml_raw_in(t, "raw");
284 if (!traw) {
285 fprintf(stderr, _("%s: missing raw value for entry %d\n"),
286 fname, ind);
287 free(keycode);
288 return EINVAL;
289 }
290
291 if (toml_rtos(traw, &raw_str)) {
292 fprintf(stderr, _("%s: bad value `%s' for keycode\n"),
293 fname, traw);
294 free(keycode);
295 return EINVAL;
296 }
297
298 if (parse_rawir_string(fname, raw_str, &re)) {
299 free(keycode);
300 free(raw_str);
301 return EINVAL;
302 }
303
304 free(raw_str);
305 re->keycode = keycode;
306 re->next = map->raw;
307 map->raw = re;
308 }
309
310 return 0;
311 }
312
parse_toml_protocol(const char * fname,struct toml_table_t * proot,struct keymap ** keymap,bool verbose)313 static int parse_toml_protocol(const char *fname, struct toml_table_t *proot, struct keymap **keymap, bool verbose)
314 {
315 struct toml_table_t *scancodes;
316 struct toml_array_t *rawarray;
317 const char *raw, *key;
318 bool have_raw_protocol = false;
319 struct keymap *map;
320 char *p;
321 int i = 0;
322
323 map = calloc(1, sizeof(*map));
324 if (!map) {
325 perror("parse_toml_protocol");
326 return ENOMEM;
327 }
328 *keymap = map;
329
330 raw = toml_raw_in(proot, "protocol");
331 if (!raw) {
332 fprintf(stderr, _("%s: protocol missing\n"), fname);
333 free_keymap(map);
334 return EINVAL;
335 }
336
337 if (toml_rtos(raw, &p)) {
338 fprintf(stderr, _("%s: bad value `%s' for protocol\n"), fname, raw);
339 free_keymap(map);
340 return EINVAL;
341 }
342
343 map->protocol = p;
344 if (!strcmp(p, "raw"))
345 have_raw_protocol = true;
346
347 raw = toml_raw_in(proot, "variant");
348 if (raw) {
349 if (toml_rtos(raw, &p)) {
350 fprintf(stderr, _("%s: bad value `%s' for variant\n"), fname, raw);
351 free_keymap(map);
352 return EINVAL;
353 }
354
355 map->variant = p;
356 }
357
358 raw = toml_raw_in(proot, "name");
359 if (raw) {
360 if (toml_rtos(raw, &p)) {
361 fprintf(stderr, _("%s: bad value `%s' for name\n"), fname, raw);
362 free_keymap(map);
363 return EINVAL;
364 }
365
366 map->name = p;
367 }
368
369 rawarray = toml_array_in(proot, "raw");
370 if (rawarray) {
371 if (toml_raw_in(proot, "scancodes")) {
372 fprintf(stderr, _("Cannot have both [raw] and [scancode] sections"));
373 free_keymap(map);
374 return EINVAL;
375 }
376 if (!have_raw_protocol) {
377 fprintf(stderr, _("Keymap with raw entries must have raw protocol"));
378 free_keymap(map);
379 return EINVAL;
380 }
381 int err = parse_toml_raw_part(fname, rawarray, map, verbose);
382 if (err != 0) {
383 free_keymap(map);
384 return err;
385 }
386 } else if (have_raw_protocol) {
387 fprintf(stderr, _("Keymap with raw protocol must have raw entries"));
388 free_keymap(map);
389 return EINVAL;
390 }
391
392 for (i = 0; (key = toml_key_in(proot, i)) != NULL; i++) {
393 int64_t value;
394
395 raw = toml_raw_in(proot, key);
396 if (!toml_rtoi(raw, &value)) {
397 struct protocol_param *param;
398
399 param = malloc(sizeof(*param));
400 param->name = strdup(key);
401 param->value = value;
402 param->next = map->param;
403 map->param = param;
404 if (verbose)
405 fprintf(stderr, _("%s: protocol parameter %s=%ld\n"), fname, param->name, param->value);
406 }
407 }
408
409 scancodes = toml_table_in(proot, "scancodes");
410 if (!scancodes) {
411 if (verbose)
412 fprintf(stderr, _("%s: no [protocols.scancodes] section\n"), fname);
413 return 0;
414 }
415
416 struct scancode_entry **next = &map->scancode;
417 i = 0;
418
419 for (;;) {
420 struct scancode_entry *se;
421 const char *scancode;
422 char *keycode;
423
424 scancode = toml_key_in(scancodes, i++);
425 if (!scancode)
426 break;
427
428 raw = toml_raw_in(scancodes, scancode);
429 if (!raw) {
430 fprintf(stderr, _("%s: invalid value `%s'\n"), fname, scancode);
431 free_keymap(map);
432 return EINVAL;
433 }
434
435 if (toml_rtos(raw, &keycode)) {
436 fprintf(stderr, _("%s: bad value `%s' for keycode\n"),
437 fname, keycode);
438 free_keymap(map);
439 return EINVAL;
440 }
441
442 se = calloc(1, sizeof(*se));
443 if (!se) {
444 perror("parse_keymap");
445 free(keycode);
446 free_keymap(map);
447 return ENOMEM;
448 }
449
450 se->scancode = strtoull(scancode, NULL, 0);
451 se->keycode = keycode;
452 *next = se;
453 next = &se->next;
454 }
455
456 return 0;
457 }
458
parse_toml_keymap(char * fname,struct keymap ** keymap,bool verbose)459 static int parse_toml_keymap(char *fname, struct keymap **keymap, bool verbose)
460 {
461 struct toml_table_t *root, *proot;
462 struct toml_array_t *arr;
463 int ret, i = 0;
464 char buf[200];
465 FILE *fin;
466
467 if (verbose)
468 fprintf(stderr, _("Parsing %s keycode file as toml\n"), fname);
469
470 fin = fopen(fname, "r");
471 if (!fin) {
472 fprintf(stderr, _("%s: error: cannot open: %m\n"), fname);
473 return EINVAL;
474 }
475
476 root = toml_parse_file(fin, buf, sizeof(buf));
477 fclose(fin);
478 if (!root) {
479 fprintf(stderr, _("%s: error: %s\n"), fname, buf);
480 return EINVAL;
481 }
482
483 arr = toml_array_in(root, "protocols");
484 if (!arr) {
485 fprintf(stderr, _("%s: missing [protocols] section\n"), fname);
486 goto out;
487 }
488
489 struct keymap *map = NULL;
490
491 for (;;) {
492 struct keymap *cur_map;
493
494 proot = toml_table_at(arr, i);
495 if (!proot)
496 break;
497
498 ret = parse_toml_protocol(fname, proot, &cur_map, verbose);
499 if (ret)
500 goto out;
501
502 if (!map) {
503 map = cur_map;
504 } else {
505 cur_map->next = map->next;
506 map->next = cur_map;
507 }
508 i++;
509 }
510
511 if (i == 0) {
512 fprintf(stderr, _("%s: no protocols found\n"), fname);
513 goto out;
514 }
515
516 toml_free(root);
517 *keymap = map;
518 return 0;
519 out:
520 toml_free(root);
521 return EINVAL;
522 }
523
parse_keymap(char * fname,struct keymap ** keymap,bool verbose)524 int parse_keymap(char *fname, struct keymap **keymap, bool verbose)
525 {
526 size_t len = strlen(fname);
527
528 if (len >= 5 && strcasecmp(fname + len - 5, ".toml") == 0)
529 return parse_toml_keymap(fname, keymap, verbose);
530
531 return parse_plain_keymap(fname, keymap, verbose);
532 }
533
keymap_param(struct keymap * map,const char * name,int fallback)534 int keymap_param(struct keymap *map, const char *name, int fallback)
535 {
536 struct protocol_param *param;
537
538 for (param = map->param; param; param = param->next) {
539 if (!strcmp(param->name, name))
540 return param->value;
541 }
542
543 return fallback;
544 }
545