diff options
Diffstat (limited to 'src/shared/kbd-util.c')
-rw-r--r-- | src/shared/kbd-util.c | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/shared/kbd-util.c b/src/shared/kbd-util.c new file mode 100644 index 0000000..2f2d161 --- /dev/null +++ b/src/shared/kbd-util.c @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "errno-util.h" +#include "kbd-util.h" +#include "log.h" +#include "nulstr-util.h" +#include "path-util.h" +#include "recurse-dir.h" +#include "set.h" +#include "string-util.h" +#include "strv.h" +#include "utf8.h" + +struct recurse_dir_userdata { + const char *keymap_name; + Set *keymaps; +}; + +static int keymap_recurse_dir_callback( + RecurseDirEvent event, + const char *path, + int dir_fd, + int inode_fd, + const struct dirent *de, + const struct statx *sx, + void *userdata) { + + struct recurse_dir_userdata *data = userdata; + _cleanup_free_ char *p = NULL; + int r; + + assert(de); + + /* If 'keymap_name' is non-NULL, return true if keymap 'keymap_name' is found. Otherwise, add all + * keymaps to 'keymaps'. */ + + if (event != RECURSE_DIR_ENTRY) + return RECURSE_DIR_CONTINUE; + + if (!IN_SET(de->d_type, DT_REG, DT_LNK)) + return RECURSE_DIR_CONTINUE; + + const char *e = endswith(de->d_name, ".map") ?: endswith(de->d_name, ".map.gz"); + if (!e) + return RECURSE_DIR_CONTINUE; + + p = strndup(de->d_name, e - de->d_name); + if (!p) + return -ENOMEM; + + if (data->keymap_name) + return streq(p, data->keymap_name) ? 1 : RECURSE_DIR_CONTINUE; + + assert(data->keymaps); + + if (!keymap_is_valid(p)) + return 0; + + r = set_consume(data->keymaps, TAKE_PTR(p)); + if (r < 0) + return r; + + return RECURSE_DIR_CONTINUE; +} + +int get_keymaps(char ***ret) { + _cleanup_set_free_free_ Set *keymaps = NULL; + int r; + + keymaps = set_new(&string_hash_ops); + if (!keymaps) + return -ENOMEM; + + NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { + r = recurse_dir_at( + AT_FDCWD, + dir, + /* statx_mask= */ 0, + /* n_depth_max= */ UINT_MAX, + RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, + keymap_recurse_dir_callback, + &(struct recurse_dir_userdata) { + .keymaps = keymaps, + }); + if (r == -ENOENT) + continue; + if (ERRNO_IS_NEG_RESOURCE(r)) + return log_warning_errno(r, "Failed to read keymap list from %s: %m", dir); + if (r < 0) + log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", dir); + } + + _cleanup_strv_free_ char **l = set_get_strv(keymaps); + if (!l) + return -ENOMEM; + + keymaps = set_free(keymaps); /* If we got the strv above, then do a set_free() rather than + * set_free_free() since the entries of the set are now owned by the + * strv */ + + if (strv_isempty(l)) + return -ENOENT; + + strv_sort(l); + + *ret = TAKE_PTR(l); + return 0; +} + +bool keymap_is_valid(const char *name) { + if (isempty(name)) + return false; + + if (strlen(name) >= 128) + return false; + + if (!utf8_is_valid(name)) + return false; + + if (!filename_is_valid(name)) + return false; + + if (!string_is_safe(name)) + return false; + + return true; +} + +int keymap_exists(const char *name) { + int r; + + if (!keymap_is_valid(name)) + return -EINVAL; + + NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { + r = recurse_dir_at( + AT_FDCWD, + dir, + /* statx_mask= */ 0, + /* n_depth_max= */ UINT_MAX, + RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, + keymap_recurse_dir_callback, + &(struct recurse_dir_userdata) { + .keymap_name = name, + }); + if (r > 0) + return true; + if (ERRNO_IS_NEG_RESOURCE(r)) + return r; + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", dir); + } + + return false; +} |