summaryrefslogtreecommitdiffstats
path: root/src/shared/kbd-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/kbd-util.c')
-rw-r--r--src/shared/kbd-util.c155
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;
+}