]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/locale/keymap-util.c
locale: rename context_free() to context_clear()
[thirdparty/systemd.git] / src / locale / keymap-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
4897d1dc
ZJS
2
3#include <errno.h>
0d536673 4#include <stdio_ext.h>
4897d1dc
ZJS
5#include <string.h>
6#include <unistd.h>
7
8#include "def.h"
9#include "env-util.h"
10#include "fd-util.h"
11#include "fileio-label.h"
12#include "fileio.h"
13#include "keymap-util.h"
14#include "locale-util.h"
15#include "macro.h"
16#include "mkdir.h"
17#include "string-util.h"
18#include "strv.h"
19
20static bool startswith_comma(const char *s, const char *prefix) {
5ad327dd
ZJS
21 s = startswith(s, prefix);
22 if (!s)
23 return false;
4897d1dc 24
4c701096 25 return IN_SET(*s, ',', '\0');
4897d1dc
ZJS
26}
27
28static const char* strnulldash(const char *s) {
29 return isempty(s) || streq(s, "-") ? NULL : s;
30}
31
cabffaf8
ZJS
32static const char* systemd_kbd_model_map(void) {
33 const char* s;
34
35 s = getenv("SYSTEMD_KBD_MODEL_MAP");
36 if (s)
37 return s;
38
39 return SYSTEMD_KBD_MODEL_MAP;
40}
41
42static const char* systemd_language_fallback_map(void) {
43 const char* s;
44
45 s = getenv("SYSTEMD_LANGUAGE_FALLBACK_MAP");
46 if (s)
47 return s;
48
49 return SYSTEMD_LANGUAGE_FALLBACK_MAP;
50}
51
4897d1dc
ZJS
52static void context_free_x11(Context *c) {
53 c->x11_layout = mfree(c->x11_layout);
54 c->x11_options = mfree(c->x11_options);
55 c->x11_model = mfree(c->x11_model);
56 c->x11_variant = mfree(c->x11_variant);
57}
58
59static void context_free_vconsole(Context *c) {
60 c->vc_keymap = mfree(c->vc_keymap);
61 c->vc_keymap_toggle = mfree(c->vc_keymap_toggle);
62}
63
64static void context_free_locale(Context *c) {
65 int p;
66
67 for (p = 0; p < _VARIABLE_LC_MAX; p++)
68 c->locale[p] = mfree(c->locale[p]);
69}
70
6804d7a8 71void context_clear(Context *c) {
4897d1dc
ZJS
72 context_free_locale(c);
73 context_free_x11(c);
74 context_free_vconsole(c);
65d34266
YW
75
76 sd_bus_message_unref(c->locale_cache);
77 sd_bus_message_unref(c->x11_cache);
78 sd_bus_message_unref(c->vc_cache);
4897d1dc
ZJS
79};
80
df4fd2c7 81void locale_simplify(char *locale[_VARIABLE_LC_MAX]) {
4897d1dc
ZJS
82 int p;
83
84 for (p = VARIABLE_LANG+1; p < _VARIABLE_LC_MAX; p++)
df4fd2c7
YW
85 if (isempty(locale[p]) || streq_ptr(locale[VARIABLE_LANG], locale[p]))
86 locale[p] = mfree(locale[p]);
4897d1dc
ZJS
87}
88
df4fd2c7
YW
89int locale_read_data(Context *c, sd_bus_message *m) {
90 struct stat st;
4897d1dc
ZJS
91 int r;
92
df4fd2c7 93 /* Do not try to re-read the file within single bus operation. */
65d34266
YW
94 if (m) {
95 if (m == c->locale_cache)
96 return 0;
4897d1dc 97
65d34266
YW
98 sd_bus_message_unref(c->locale_cache);
99 c->locale_cache = sd_bus_message_ref(m);
100 }
df4fd2c7
YW
101
102 r = stat("/etc/locale.conf", &st);
103 if (r < 0 && errno != ENOENT)
104 return -errno;
105
106 if (r >= 0) {
107 usec_t t;
108
109 /* If mtime is not changed, then we do not need to re-read the file. */
110 t = timespec_load(&st.st_mtim);
111 if (c->locale_mtime != USEC_INFINITY && t == c->locale_mtime)
112 return 0;
4897d1dc 113
df4fd2c7
YW
114 c->locale_mtime = t;
115 context_free_locale(c);
116
aa8fbc74 117 r = parse_env_file(NULL, "/etc/locale.conf",
df4fd2c7
YW
118 "LANG", &c->locale[VARIABLE_LANG],
119 "LANGUAGE", &c->locale[VARIABLE_LANGUAGE],
120 "LC_CTYPE", &c->locale[VARIABLE_LC_CTYPE],
121 "LC_NUMERIC", &c->locale[VARIABLE_LC_NUMERIC],
122 "LC_TIME", &c->locale[VARIABLE_LC_TIME],
123 "LC_COLLATE", &c->locale[VARIABLE_LC_COLLATE],
124 "LC_MONETARY", &c->locale[VARIABLE_LC_MONETARY],
125 "LC_MESSAGES", &c->locale[VARIABLE_LC_MESSAGES],
126 "LC_PAPER", &c->locale[VARIABLE_LC_PAPER],
127 "LC_NAME", &c->locale[VARIABLE_LC_NAME],
128 "LC_ADDRESS", &c->locale[VARIABLE_LC_ADDRESS],
129 "LC_TELEPHONE", &c->locale[VARIABLE_LC_TELEPHONE],
130 "LC_MEASUREMENT", &c->locale[VARIABLE_LC_MEASUREMENT],
13df9c39 131 "LC_IDENTIFICATION", &c->locale[VARIABLE_LC_IDENTIFICATION]);
df4fd2c7
YW
132 if (r < 0)
133 return r;
134 } else {
4897d1dc
ZJS
135 int p;
136
df4fd2c7
YW
137 c->locale_mtime = USEC_INFINITY;
138 context_free_locale(c);
139
4897d1dc
ZJS
140 /* Fill in what we got passed from systemd. */
141 for (p = 0; p < _VARIABLE_LC_MAX; p++) {
142 const char *name;
143
144 name = locale_variable_to_string(p);
145 assert(name);
146
147 r = free_and_strdup(&c->locale[p], empty_to_null(getenv(name)));
148 if (r < 0)
149 return r;
150 }
4897d1dc
ZJS
151 }
152
df4fd2c7
YW
153 locale_simplify(c->locale);
154 return 0;
4897d1dc
ZJS
155}
156
df4fd2c7
YW
157int vconsole_read_data(Context *c, sd_bus_message *m) {
158 struct stat st;
159 usec_t t;
4897d1dc
ZJS
160 int r;
161
df4fd2c7 162 /* Do not try to re-read the file within single bus operation. */
65d34266
YW
163 if (m) {
164 if (m == c->vc_cache)
165 return 0;
df4fd2c7 166
65d34266
YW
167 sd_bus_message_unref(c->vc_cache);
168 c->vc_cache = sd_bus_message_ref(m);
169 }
df4fd2c7
YW
170
171 if (stat("/etc/vconsole.conf", &st) < 0) {
172 if (errno != ENOENT)
173 return -errno;
174
175 c->vc_mtime = USEC_INFINITY;
176 context_free_vconsole(c);
177 return 0;
178 }
179
180 /* If mtime is not changed, then we do not need to re-read */
181 t = timespec_load(&st.st_mtim);
182 if (c->vc_mtime != USEC_INFINITY && t == c->vc_mtime)
183 return 0;
184
185 c->vc_mtime = t;
4897d1dc
ZJS
186 context_free_vconsole(c);
187
aa8fbc74 188 r = parse_env_file(NULL, "/etc/vconsole.conf",
4897d1dc 189 "KEYMAP", &c->vc_keymap,
13df9c39 190 "KEYMAP_TOGGLE", &c->vc_keymap_toggle);
df4fd2c7 191 if (r < 0)
4897d1dc
ZJS
192 return r;
193
194 return 0;
195}
196
df4fd2c7
YW
197int x11_read_data(Context *c, sd_bus_message *m) {
198 _cleanup_fclose_ FILE *f = NULL;
4897d1dc 199 bool in_section = false;
df4fd2c7
YW
200 struct stat st;
201 usec_t t;
4897d1dc
ZJS
202 int r;
203
df4fd2c7 204 /* Do not try to re-read the file within single bus operation. */
65d34266
YW
205 if (m) {
206 if (m == c->x11_cache)
207 return 0;
df4fd2c7 208
65d34266
YW
209 sd_bus_message_unref(c->x11_cache);
210 c->x11_cache = sd_bus_message_ref(m);
211 }
df4fd2c7
YW
212
213 if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) < 0) {
214 if (errno != ENOENT)
215 return -errno;
216
217 c->x11_mtime = USEC_INFINITY;
218 context_free_x11(c);
219 return 0;
220 }
221
222 /* If mtime is not changed, then we do not need to re-read */
223 t = timespec_load(&st.st_mtim);
224 if (c->x11_mtime != USEC_INFINITY && t == c->x11_mtime)
225 return 0;
226
227 c->x11_mtime = t;
4897d1dc
ZJS
228 context_free_x11(c);
229
230 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
231 if (!f)
df4fd2c7 232 return -errno;
4897d1dc 233
1d47b569
LP
234 for (;;) {
235 _cleanup_free_ char *line = NULL;
4897d1dc
ZJS
236 char *l;
237
1d47b569
LP
238 r = read_line(f, LONG_LINE_MAX, &line);
239 if (r < 0)
240 return r;
241 if (r == 0)
242 break;
4897d1dc 243
1d47b569 244 l = strstrip(line);
4c701096 245 if (IN_SET(l[0], 0, '#'))
4897d1dc
ZJS
246 continue;
247
248 if (in_section && first_word(l, "Option")) {
249 _cleanup_strv_free_ char **a = NULL;
250
251 r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES);
252 if (r < 0)
253 return r;
254
255 if (strv_length(a) == 3) {
256 char **p = NULL;
257
258 if (streq(a[1], "XkbLayout"))
259 p = &c->x11_layout;
260 else if (streq(a[1], "XkbModel"))
261 p = &c->x11_model;
262 else if (streq(a[1], "XkbVariant"))
263 p = &c->x11_variant;
264 else if (streq(a[1], "XkbOptions"))
265 p = &c->x11_options;
266
267 if (p) {
f9ecfd3b 268 free_and_replace(*p, a[2]);
4897d1dc
ZJS
269 }
270 }
271
272 } else if (!in_section && first_word(l, "Section")) {
273 _cleanup_strv_free_ char **a = NULL;
274
275 r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES);
276 if (r < 0)
277 return -ENOMEM;
278
279 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
280 in_section = true;
281
282 } else if (in_section && first_word(l, "EndSection"))
283 in_section = false;
284 }
285
286 return 0;
287}
288
4897d1dc 289int locale_write_data(Context *c, char ***settings) {
4897d1dc 290 _cleanup_strv_free_ char **l = NULL;
df4fd2c7
YW
291 struct stat st;
292 int r, p;
4897d1dc
ZJS
293
294 /* Set values will be returned as strv in *settings on success. */
295
4897d1dc
ZJS
296 for (p = 0; p < _VARIABLE_LC_MAX; p++) {
297 _cleanup_free_ char *t = NULL;
298 char **u;
299 const char *name;
300
301 name = locale_variable_to_string(p);
302 assert(name);
303
df4fd2c7 304 if (isempty(c->locale[p]))
4897d1dc 305 continue;
4897d1dc
ZJS
306
307 if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0)
308 return -ENOMEM;
309
310 u = strv_env_set(l, t);
311 if (!u)
312 return -ENOMEM;
313
130d3d22 314 strv_free_and_replace(l, u);
4897d1dc
ZJS
315 }
316
317 if (strv_isempty(l)) {
318 if (unlink("/etc/locale.conf") < 0)
319 return errno == ENOENT ? 0 : -errno;
320
df4fd2c7 321 c->locale_mtime = USEC_INFINITY;
4897d1dc
ZJS
322 return 0;
323 }
324
325 r = write_env_file_label("/etc/locale.conf", l);
326 if (r < 0)
327 return r;
328
ae2a15bc 329 *settings = TAKE_PTR(l);
df4fd2c7
YW
330
331 if (stat("/etc/locale.conf", &st) >= 0)
332 c->locale_mtime = timespec_load(&st.st_mtim);
333
4897d1dc
ZJS
334 return 0;
335}
336
337int vconsole_write_data(Context *c) {
4897d1dc 338 _cleanup_strv_free_ char **l = NULL;
df4fd2c7
YW
339 struct stat st;
340 int r;
4897d1dc 341
aa8fbc74 342 r = load_env_file(NULL, "/etc/vconsole.conf", &l);
4897d1dc
ZJS
343 if (r < 0 && r != -ENOENT)
344 return r;
345
346 if (isempty(c->vc_keymap))
347 l = strv_env_unset(l, "KEYMAP");
348 else {
349 _cleanup_free_ char *s = NULL;
350 char **u;
351
352 s = strappend("KEYMAP=", c->vc_keymap);
353 if (!s)
354 return -ENOMEM;
355
356 u = strv_env_set(l, s);
357 if (!u)
358 return -ENOMEM;
359
130d3d22 360 strv_free_and_replace(l, u);
4897d1dc
ZJS
361 }
362
363 if (isempty(c->vc_keymap_toggle))
364 l = strv_env_unset(l, "KEYMAP_TOGGLE");
365 else {
366 _cleanup_free_ char *s = NULL;
367 char **u;
368
369 s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
370 if (!s)
371 return -ENOMEM;
372
373 u = strv_env_set(l, s);
374 if (!u)
375 return -ENOMEM;
376
130d3d22 377 strv_free_and_replace(l, u);
4897d1dc
ZJS
378 }
379
380 if (strv_isempty(l)) {
381 if (unlink("/etc/vconsole.conf") < 0)
382 return errno == ENOENT ? 0 : -errno;
383
df4fd2c7 384 c->vc_mtime = USEC_INFINITY;
4897d1dc
ZJS
385 return 0;
386 }
387
df4fd2c7
YW
388 r = write_env_file_label("/etc/vconsole.conf", l);
389 if (r < 0)
390 return r;
391
392 if (stat("/etc/vconsole.conf", &st) >= 0)
393 c->vc_mtime = timespec_load(&st.st_mtim);
394
395 return 0;
4897d1dc
ZJS
396}
397
398int x11_write_data(Context *c) {
399 _cleanup_fclose_ FILE *f = NULL;
400 _cleanup_free_ char *temp_path = NULL;
df4fd2c7 401 struct stat st;
4897d1dc
ZJS
402 int r;
403
404 if (isempty(c->x11_layout) &&
405 isempty(c->x11_model) &&
406 isempty(c->x11_variant) &&
407 isempty(c->x11_options)) {
408
409 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
410 return errno == ENOENT ? 0 : -errno;
411
df4fd2c7 412 c->vc_mtime = USEC_INFINITY;
4897d1dc
ZJS
413 return 0;
414 }
415
416 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
417
418 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
419 if (r < 0)
420 return r;
421
0d536673
LP
422 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
423 (void) fchmod(fileno(f), 0644);
4897d1dc 424
0d536673
LP
425 fputs("# Written by systemd-localed(8), read by systemd-localed and Xorg. It's\n"
426 "# probably wise not to edit this file manually. Use localectl(1) to\n"
427 "# instruct systemd-localed to update it.\n"
428 "Section \"InputClass\"\n"
429 " Identifier \"system-keyboard\"\n"
430 " MatchIsKeyboard \"on\"\n", f);
4897d1dc
ZJS
431
432 if (!isempty(c->x11_layout))
433 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
434
435 if (!isempty(c->x11_model))
436 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
437
438 if (!isempty(c->x11_variant))
439 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
440
441 if (!isempty(c->x11_options))
442 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
443
0d536673 444 fputs("EndSection\n", f);
4897d1dc 445
0675e94a 446 r = fflush_sync_and_check(f);
4897d1dc
ZJS
447 if (r < 0)
448 goto fail;
449
450 if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
451 r = -errno;
452 goto fail;
453 }
454
df4fd2c7
YW
455 if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) >= 0)
456 c->x11_mtime = timespec_load(&st.st_mtim);
457
4897d1dc
ZJS
458 return 0;
459
460fail:
4897d1dc
ZJS
461 if (temp_path)
462 (void) unlink(temp_path);
463
464 return r;
465}
466
467static int read_next_mapping(const char* filename,
468 unsigned min_fields, unsigned max_fields,
469 FILE *f, unsigned *n, char ***a) {
470 assert(f);
471 assert(n);
472 assert(a);
473
474 for (;;) {
1d47b569
LP
475 _cleanup_free_ char *line = NULL;
476 size_t length;
4897d1dc
ZJS
477 char *l, **b;
478 int r;
4897d1dc 479
1d47b569
LP
480 r = read_line(f, LONG_LINE_MAX, &line);
481 if (r < 0)
482 return r;
483 if (r == 0)
484 break;
4897d1dc
ZJS
485
486 (*n)++;
487
488 l = strstrip(line);
4c701096 489 if (IN_SET(l[0], 0, '#'))
4897d1dc
ZJS
490 continue;
491
492 r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_QUOTES);
493 if (r < 0)
494 return r;
495
496 length = strv_length(b);
497 if (length < min_fields || length > max_fields) {
498 log_error("Invalid line %s:%u, ignoring.", filename, *n);
499 strv_free(b);
500 continue;
501
502 }
503
504 *a = b;
505 return 1;
506 }
1d47b569
LP
507
508 return 0;
4897d1dc
ZJS
509}
510
511int vconsole_convert_to_x11(Context *c) {
cabffaf8 512 const char *map;
6f3287b3 513 int modified = -1;
4897d1dc 514
cabffaf8
ZJS
515 map = systemd_kbd_model_map();
516
4897d1dc 517 if (isempty(c->vc_keymap)) {
4897d1dc
ZJS
518 modified =
519 !isempty(c->x11_layout) ||
520 !isempty(c->x11_model) ||
521 !isempty(c->x11_variant) ||
522 !isempty(c->x11_options);
523
524 context_free_x11(c);
525 } else {
526 _cleanup_fclose_ FILE *f = NULL;
527 unsigned n = 0;
528
cabffaf8 529 f = fopen(map, "re");
4897d1dc
ZJS
530 if (!f)
531 return -errno;
532
533 for (;;) {
534 _cleanup_strv_free_ char **a = NULL;
535 int r;
536
cabffaf8 537 r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a);
4897d1dc
ZJS
538 if (r < 0)
539 return r;
540 if (r == 0)
541 break;
542
543 if (!streq(c->vc_keymap, a[0]))
544 continue;
545
546 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
547 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
548 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
549 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
550
551 if (free_and_strdup(&c->x11_layout, strnulldash(a[1])) < 0 ||
552 free_and_strdup(&c->x11_model, strnulldash(a[2])) < 0 ||
553 free_and_strdup(&c->x11_variant, strnulldash(a[3])) < 0 ||
554 free_and_strdup(&c->x11_options, strnulldash(a[4])) < 0)
555 return -ENOMEM;
556
557 modified = true;
558 }
559
560 break;
561 }
562 }
563
6f3287b3 564 if (modified > 0)
4897d1dc
ZJS
565 log_info("Changing X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
566 strempty(c->x11_layout),
567 strempty(c->x11_model),
568 strempty(c->x11_variant),
569 strempty(c->x11_options));
6f3287b3
ZJS
570 else if (modified < 0)
571 log_notice("X11 keyboard layout was not modified: no conversion found for \"%s\".",
572 c->vc_keymap);
4897d1dc 573 else
6f3287b3 574 log_debug("X11 keyboard layout did not need to be modified.");
4897d1dc 575
6f3287b3 576 return modified > 0;
4897d1dc
ZJS
577}
578
579int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap) {
580 const char *dir;
581 _cleanup_free_ char *n;
582
583 if (x11_variant)
605405c6 584 n = strjoin(x11_layout, "-", x11_variant);
4897d1dc
ZJS
585 else
586 n = strdup(x11_layout);
587 if (!n)
588 return -ENOMEM;
589
590 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
591 _cleanup_free_ char *p = NULL, *pz = NULL;
592 bool uncompressed;
593
605405c6
ZJS
594 p = strjoin(dir, "xkb/", n, ".map");
595 pz = strjoin(dir, "xkb/", n, ".map.gz");
4897d1dc
ZJS
596 if (!p || !pz)
597 return -ENOMEM;
598
599 uncompressed = access(p, F_OK) == 0;
600 if (uncompressed || access(pz, F_OK) == 0) {
601 log_debug("Found converted keymap %s at %s",
602 n, uncompressed ? p : pz);
603
ae2a15bc 604 *new_keymap = TAKE_PTR(n);
4897d1dc
ZJS
605 return 1;
606 }
607 }
608
609 return 0;
610}
611
6a6e9c03 612int find_legacy_keymap(Context *c, char **ret) {
cabffaf8
ZJS
613 const char *map;
614 _cleanup_fclose_ FILE *f = NULL;
6a6e9c03 615 _cleanup_free_ char *new_keymap = NULL;
4897d1dc
ZJS
616 unsigned n = 0;
617 unsigned best_matching = 0;
618 int r;
619
5ad327dd
ZJS
620 assert(!isempty(c->x11_layout));
621
cabffaf8
ZJS
622 map = systemd_kbd_model_map();
623
624 f = fopen(map, "re");
4897d1dc
ZJS
625 if (!f)
626 return -errno;
627
628 for (;;) {
629 _cleanup_strv_free_ char **a = NULL;
630 unsigned matching = 0;
631
cabffaf8 632 r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a);
4897d1dc
ZJS
633 if (r < 0)
634 return r;
635 if (r == 0)
636 break;
637
638 /* Determine how well matching this entry is */
5ad327dd 639 if (streq(c->x11_layout, a[1]))
4897d1dc
ZJS
640 /* If we got an exact match, this is best */
641 matching = 10;
642 else {
643 /* We have multiple X layouts, look for an
644 * entry that matches our key with everything
645 * but the first layout stripped off. */
646 if (startswith_comma(c->x11_layout, a[1]))
647 matching = 5;
648 else {
649 char *x;
650
651 /* If that didn't work, strip off the
652 * other layouts from the entry, too */
653 x = strndupa(a[1], strcspn(a[1], ","));
654 if (startswith_comma(c->x11_layout, x))
655 matching = 1;
656 }
657 }
658
659 if (matching > 0) {
660 if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
661 matching++;
662
663 if (streq_ptr(c->x11_variant, a[3])) {
664 matching++;
665
666 if (streq_ptr(c->x11_options, a[4]))
667 matching++;
668 }
669 }
670 }
671
672 /* The best matching entry so far, then let's save that */
673 if (matching >= MAX(best_matching, 1u)) {
674 log_debug("Found legacy keymap %s with score %u",
675 a[0], matching);
676
677 if (matching > best_matching) {
678 best_matching = matching;
679
6a6e9c03 680 r = free_and_strdup(&new_keymap, a[0]);
4897d1dc
ZJS
681 if (r < 0)
682 return r;
683 }
684 }
685 }
686
687 if (best_matching < 10 && c->x11_layout) {
688 /* The best match is only the first part of the X11
689 * keymap. Check if we have a converted map which
690 * matches just the first layout.
691 */
692 char *l, *v = NULL, *converted;
693
694 l = strndupa(c->x11_layout, strcspn(c->x11_layout, ","));
695 if (c->x11_variant)
696 v = strndupa(c->x11_variant, strcspn(c->x11_variant, ","));
697 r = find_converted_keymap(l, v, &converted);
698 if (r < 0)
699 return r;
6a6e9c03
ZJS
700 if (r > 0)
701 free_and_replace(new_keymap, converted);
4897d1dc
ZJS
702 }
703
6a6e9c03
ZJS
704 *ret = TAKE_PTR(new_keymap);
705 return (bool) *ret;
4897d1dc
ZJS
706}
707
708int find_language_fallback(const char *lang, char **language) {
cabffaf8 709 const char *map;
4897d1dc
ZJS
710 _cleanup_fclose_ FILE *f = NULL;
711 unsigned n = 0;
712
aa63b56f 713 assert(lang);
4897d1dc
ZJS
714 assert(language);
715
cabffaf8
ZJS
716 map = systemd_language_fallback_map();
717
718 f = fopen(map, "re");
4897d1dc
ZJS
719 if (!f)
720 return -errno;
721
722 for (;;) {
723 _cleanup_strv_free_ char **a = NULL;
724 int r;
725
cabffaf8 726 r = read_next_mapping(map, 2, 2, f, &n, &a);
4897d1dc
ZJS
727 if (r <= 0)
728 return r;
729
730 if (streq(lang, a[0])) {
731 assert(strv_length(a) == 2);
1cc6c93a 732 *language = TAKE_PTR(a[1]);
4897d1dc
ZJS
733 return 1;
734 }
735 }
736
737 assert_not_reached("should not be here");
738}
739
740int x11_convert_to_vconsole(Context *c) {
741 bool modified = false;
742
743 if (isempty(c->x11_layout)) {
4897d1dc
ZJS
744 modified =
745 !isempty(c->vc_keymap) ||
746 !isempty(c->vc_keymap_toggle);
747
aa63b56f 748 context_free_vconsole(c);
4897d1dc 749 } else {
6a837b03 750 _cleanup_free_ char *new_keymap = NULL;
4897d1dc
ZJS
751 int r;
752
753 r = find_converted_keymap(c->x11_layout, c->x11_variant, &new_keymap);
754 if (r < 0)
755 return r;
756 else if (r == 0) {
757 r = find_legacy_keymap(c, &new_keymap);
758 if (r < 0)
759 return r;
760 }
5ad327dd
ZJS
761 if (r == 0)
762 /* We search for layout-variant match first, but then we also look
763 * for anything which matches just the layout. So it's accurate to say
764 * that we couldn't find anything which matches the layout. */
765 log_notice("No conversion to virtual console map found for \"%s\".",
766 c->x11_layout);
4897d1dc
ZJS
767
768 if (!streq_ptr(c->vc_keymap, new_keymap)) {
6a837b03 769 free_and_replace(c->vc_keymap, new_keymap);
4897d1dc
ZJS
770 c->vc_keymap_toggle = mfree(c->vc_keymap_toggle);
771 modified = true;
6a837b03 772 }
4897d1dc
ZJS
773 }
774
775 if (modified)
776 log_info("Changing virtual console keymap to '%s' toggle '%s'",
777 strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
778 else
779 log_debug("Virtual console keymap was not modified.");
780
781 return modified;
782}