blob: 6222ef4fc81c4d50864833e4ec76e7dfc72e0227 [file] [log] [blame]
/* 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_freep) 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 < 0) {
if (r == -ENOENT)
continue;
if (ERRNO_IS_RESOURCE(r))
return log_warning_errno(r, "Failed to read keymap list from %s: %m", dir);
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 = 0;
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 == -ENOENT)
continue;
if (ERRNO_IS_RESOURCE(r))
return r;
if (r < 0) {
log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", dir);
continue;
}
if (r > 0)
break;
}
return r > 0;
}