Simplify reporting of invalid config paths

Do this at a well defined point, instead of randomly the first time they're
queried.
This commit is contained in:
ridiculousfish 2019-05-01 17:31:22 -07:00
parent 72e43a514b
commit 649d3ac101
3 changed files with 61 additions and 51 deletions

View file

@ -730,6 +730,9 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
init_input();
// Complain about invalid config paths.
path_emit_config_directory_errors(vars);
// Set up universal variables. The empty string means to use the default path.
assert(s_universal_variables == NULL);
s_universal_variables = new env_universal_t(L"");

View file

@ -255,8 +255,7 @@ wcstring path_apply_working_directory(const wcstring &path, const wcstring &work
/// a function) we don't want that subshell to issue the same warnings.
static void maybe_issue_path_warning(const wcstring &which_dir, const wcstring &custom_error_msg,
bool using_xdg, const wcstring &xdg_var, const wcstring &path,
int saved_errno) {
auto &vars = env_stack_t::globals();
int saved_errno, env_stack_t &vars) {
wcstring warning_var_name = L"_FISH_WARNED_" + which_dir;
if (vars.get(warning_var_name, ENV_GLOBAL | ENV_EXPORT)) {
return;
@ -278,74 +277,77 @@ static void maybe_issue_path_warning(const wcstring &which_dir, const wcstring &
ignore_result(write(STDERR_FILENO, "\n", 1));
}
static void path_create(wcstring &path, const wcstring &xdg_var, const wcstring &which_dir,
const wcstring &custom_error_msg) {
bool path_done = false;
bool using_xdg = false;
int saved_errno = 0;
/// The following type wraps up a user's "base" directories, corresponding (conceptually if not
/// actually) to XDG spec.
struct base_directory_t {
wcstring path{}; /// the path where we attempted to create the directory.
bool success{false}; /// whether creating the directory succeeded.
int err{0}; /// the error code if creating the directory failed.
bool used_xdg{false}; /// whether an XDG variable was used in resolving the direcotry.
};
/// Attempt to get a base directory, creating it if necessary. If a variable named \p xdg_var is
/// set, use that directory; otherwise use the path \p non_xdg_homepath rooted in $HOME. \return the
/// result; see the base_directory_t fields.
static base_directory_t make_base_directory(const wcstring &xdg_var,
const wchar_t *non_xdg_homepath) {
// The vars we fetch must be exported. Allowing them to be universal doesn't make sense and
// allowing that creates a lock inversion that deadlocks the shell since we're called before
// uvars are available.
const auto &vars = env_stack_t::globals();
base_directory_t result{};
const auto xdg_dir = vars.get(xdg_var, ENV_GLOBAL | ENV_EXPORT);
if (!xdg_dir.missing_or_empty()) {
using_xdg = true;
path = xdg_dir->as_string() + L"/fish";
if (create_directory(path) != -1) {
path_done = true;
} else {
saved_errno = errno;
}
result.path = xdg_dir->as_string() + L"/fish";
result.used_xdg = true;
} else {
const auto home = vars.get(L"HOME", ENV_GLOBAL | ENV_EXPORT);
if (!home.missing_or_empty()) {
path = home->as_string() +
(which_dir == L"config" ? L"/.config/fish" : L"/.local/share/fish");
if (create_directory(path) != -1) {
path_done = true;
} else {
saved_errno = errno;
}
result.path = home->as_string() + non_xdg_homepath;
}
}
if (!path_done) {
maybe_issue_path_warning(which_dir, custom_error_msg, using_xdg, xdg_var, path,
saved_errno);
path.clear();
}
return;
errno = 0;
result.success = !result.path.empty() && create_directory(result.path) != -1;
result.err = errno;
return result;
}
static const base_directory_t &get_data_directory() {
static base_directory_t s_dir = make_base_directory(L"XDG_DATA_HOME", L"/.local/share/fish");
return s_dir;
}
static const base_directory_t &get_config_directory() {
static base_directory_t s_dir = make_base_directory(L"XDG_CONFIG_HOME", L"/.config/fish");
return s_dir;
}
void path_emit_config_directory_errors(env_stack_t &vars) {
const auto &data = get_data_directory();
if (!data.success) {
maybe_issue_path_warning(L"data", _(L"Your history will not be saved."), data.used_xdg,
L"XDG_DATA_HOME", data.path, data.err, vars);
}
const auto &config = get_config_directory();
if (!config.success) {
maybe_issue_path_warning(L"config", _(L"Your personal settings will not be saved."),
config.used_xdg, L"XDG_CONFIG_HOME", config.path, config.err,
vars);
}
}
/// Cache the config path.
bool path_get_config(wcstring &path) {
static bool config_path_done = false;
static wcstring config_path(L"");
if (!config_path_done) {
path_create(config_path, L"XDG_CONFIG_HOME", L"config",
_(L"Your personal settings will not be saved."));
config_path_done = true;
}
path = config_path;
return !config_path.empty();
const auto &dir = get_config_directory();
path = dir.success ? dir.path : L"";
return dir.success;
}
/// Cache the data path.
bool path_get_data(wcstring &path) {
static bool data_path_done = false;
static wcstring data_path(L"");
if (!data_path_done) {
data_path_done = true;
path_create(data_path, L"XDG_DATA_HOME", L"data", _(L"Your history will not be saved."));
}
path = data_path;
return !data_path.empty();
const auto &dir = get_data_directory();
path = dir.success ? dir.path : L"";
return dir.success;
}
void path_make_canonical(wcstring &path) {

View file

@ -29,6 +29,11 @@ bool path_get_config(wcstring &path);
/// \return whether the directory was returned successfully
bool path_get_data(wcstring &path);
/// Emit any errors if config directories are missing.
/// Use the given environment stack to ensure this only occurs once.
class env_stack_t;
void path_emit_config_directory_errors(env_stack_t &vars);
/// Finds the full path of an executable.
///
/// Args: