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