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(); 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. // Set up universal variables. The empty string means to use the default path.
assert(s_universal_variables == NULL); assert(s_universal_variables == NULL);
s_universal_variables = new env_universal_t(L""); 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. /// 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, 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, bool using_xdg, const wcstring &xdg_var, const wcstring &path,
int saved_errno) { int saved_errno, env_stack_t &vars) {
auto &vars = env_stack_t::globals();
wcstring warning_var_name = L"_FISH_WARNED_" + which_dir; wcstring warning_var_name = L"_FISH_WARNED_" + which_dir;
if (vars.get(warning_var_name, ENV_GLOBAL | ENV_EXPORT)) { if (vars.get(warning_var_name, ENV_GLOBAL | ENV_EXPORT)) {
return; return;
@ -278,74 +277,77 @@ static void maybe_issue_path_warning(const wcstring &which_dir, const wcstring &
ignore_result(write(STDERR_FILENO, "\n", 1)); ignore_result(write(STDERR_FILENO, "\n", 1));
} }
static void path_create(wcstring &path, const wcstring &xdg_var, const wcstring &which_dir, /// The following type wraps up a user's "base" directories, corresponding (conceptually if not
const wcstring &custom_error_msg) { /// actually) to XDG spec.
bool path_done = false; struct base_directory_t {
bool using_xdg = false; wcstring path{}; /// the path where we attempted to create the directory.
int saved_errno = 0; 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 // 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 // allowing that creates a lock inversion that deadlocks the shell since we're called before
// uvars are available. // uvars are available.
const auto &vars = env_stack_t::globals(); const auto &vars = env_stack_t::globals();
base_directory_t result{};
const auto xdg_dir = vars.get(xdg_var, ENV_GLOBAL | ENV_EXPORT); const auto xdg_dir = vars.get(xdg_var, ENV_GLOBAL | ENV_EXPORT);
if (!xdg_dir.missing_or_empty()) { if (!xdg_dir.missing_or_empty()) {
using_xdg = true; result.path = xdg_dir->as_string() + L"/fish";
path = xdg_dir->as_string() + L"/fish"; result.used_xdg = true;
if (create_directory(path) != -1) {
path_done = true;
} else {
saved_errno = errno;
}
} else { } else {
const auto home = vars.get(L"HOME", ENV_GLOBAL | ENV_EXPORT); const auto home = vars.get(L"HOME", ENV_GLOBAL | ENV_EXPORT);
if (!home.missing_or_empty()) { if (!home.missing_or_empty()) {
path = home->as_string() + result.path = home->as_string() + non_xdg_homepath;
(which_dir == L"config" ? L"/.config/fish" : L"/.local/share/fish");
if (create_directory(path) != -1) {
path_done = true;
} else {
saved_errno = errno;
}
} }
} }
if (!path_done) { errno = 0;
maybe_issue_path_warning(which_dir, custom_error_msg, using_xdg, xdg_var, path, result.success = !result.path.empty() && create_directory(result.path) != -1;
saved_errno); result.err = errno;
path.clear(); return result;
} }
return; 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) { bool path_get_config(wcstring &path) {
static bool config_path_done = false; const auto &dir = get_config_directory();
static wcstring config_path(L""); path = dir.success ? dir.path : L"";
return dir.success;
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();
}
/// Cache the data path.
bool path_get_data(wcstring &path) { bool path_get_data(wcstring &path) {
static bool data_path_done = false; const auto &dir = get_data_directory();
static wcstring data_path(L""); path = dir.success ? dir.path : L"";
return dir.success;
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();
} }
void path_make_canonical(wcstring &path) { 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 /// \return whether the directory was returned successfully
bool path_get_data(wcstring &path); 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. /// Finds the full path of an executable.
/// ///
/// Args: /// Args: