diff --git a/src/builtins/cd.cpp b/src/builtins/cd.cpp index 8a2023ed9..d6a15b074 100644 --- a/src/builtins/cd.cpp +++ b/src/builtins/cd.cpp @@ -43,8 +43,8 @@ maybe_t builtin_cd(parser_t &parser, io_streams_t &streams, const wchar_t * if (argv[optind]) { dir_in = argv[optind]; } else { - auto maybe_dir_in = parser.vars().get(L"HOME"); - if (maybe_dir_in.missing_or_empty()) { + auto maybe_dir_in = parser.vars().get_unless_empty(L"HOME"); + if (!maybe_dir_in) { streams.err.append_format(_(L"%ls: Could not find home directory\n"), cmd); return STATUS_CMD_ERROR; } diff --git a/src/builtins/read.cpp b/src/builtins/read.cpp index c1fc6c4ff..8682f8658 100644 --- a/src/builtins/read.cpp +++ b/src/builtins/read.cpp @@ -566,8 +566,8 @@ maybe_t builtin_read(parser_t &parser, io_streams_t &streams, const wchar_t } if (!opts.have_delimiter) { - auto ifs = parser.vars().get(L"IFS"); - if (!ifs.missing_or_empty()) opts.delimiter = ifs->as_string(); + auto ifs = parser.vars().get_unless_empty(L"IFS"); + if (ifs) opts.delimiter = ifs->as_string(); } if (opts.delimiter.empty()) { diff --git a/src/builtins/set.cpp b/src/builtins/set.cpp index df3f8071e..4c806d4e0 100644 --- a/src/builtins/set.cpp +++ b/src/builtins/set.cpp @@ -455,8 +455,8 @@ static int builtin_set_list(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, val += expand_escape_string(history->item_at_index(i).str()); } } else { - auto var = parser.vars().get(key, compute_scope(opts)); - if (!var.missing_or_empty()) { + auto var = parser.vars().get_unless_empty(key, compute_scope(opts)); + if (var) { val = expand_escape_variable(*var); } } diff --git a/src/env.cpp b/src/env.cpp index c3d75e6af..363cd3c00 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -178,9 +178,9 @@ environment_t::~environment_t() = default; wcstring environment_t::get_pwd_slash() const { // Return "/" if PWD is missing. // See https://github.com/fish-shell/fish-shell/issues/5080 - auto pwd_var = get(L"PWD"); + auto pwd_var = get_unless_empty(L"PWD"); wcstring pwd; - if (!pwd_var.missing_or_empty()) { + if (pwd_var) { pwd = pwd_var->as_string(); } if (!string_suffixes_string(L"/", pwd)) { @@ -189,6 +189,16 @@ wcstring environment_t::get_pwd_slash() const { return pwd; } +maybe_t environment_t::get_unless_empty(const wcstring &key, + env_mode_flags_t mode) const { + if (auto variable = this->get(key, mode)) { + if (!variable->empty()) { + return variable; + } + } + return none(); +} + std::unique_ptr environment_t::get_or_null(wcstring const &key, env_mode_flags_t mode) const { auto variable = this->get(key, mode); @@ -212,20 +222,20 @@ std::vector null_environment_t::get_names(env_mode_flags_t flags) cons /// Set up the USER and HOME variable. static void setup_user(env_stack_t &vars) { auto uid = geteuid(); - auto user_var = vars.get(L"USER"); + auto user_var = vars.get_unless_empty(L"USER"); struct passwd userinfo; struct passwd *result; char buf[8192]; // If we have a $USER, we try to get the passwd entry for the name. // If that has the same UID that we use, we assume the data is correct. - if (!user_var.missing_or_empty()) { + if (user_var) { std::string unam_narrow = wcs2zstring(user_var->as_string()); int retval = getpwnam_r(unam_narrow.c_str(), &userinfo, buf, sizeof(buf), &result); if (!retval && result) { if (result->pw_uid == uid) { // The uid matches but we still might need to set $HOME. - if (vars.get(L"HOME").missing_or_empty()) { + if (!vars.get_unless_empty(L"HOME")) { if (userinfo.pw_dir) { vars.set_one(L"HOME", ENV_GLOBAL | ENV_EXPORT, str2wcstring(userinfo.pw_dir)); @@ -246,7 +256,7 @@ static void setup_user(env_stack_t &vars) { vars.set_one(L"USER", ENV_GLOBAL | ENV_EXPORT, uname); // Only change $HOME if it's empty, so we allow e.g. `HOME=(mktemp -d)`. // This is okay with common `su` and `sudo` because they set $HOME. - if (vars.get(L"HOME").missing_or_empty()) { + if (!vars.get_unless_empty(L"HOME")) { if (userinfo.pw_dir) { vars.set_one(L"HOME", ENV_GLOBAL | ENV_EXPORT, str2wcstring(userinfo.pw_dir)); } else { @@ -255,7 +265,7 @@ static void setup_user(env_stack_t &vars) { vars.set_empty(L"HOME", ENV_GLOBAL | ENV_EXPORT); } } - } else if (vars.get(L"HOME").missing_or_empty()) { + } else if (!vars.get_unless_empty(L"HOME")) { // If $USER is empty as well (which we tried to set above), we can't get $HOME. vars.set_empty(L"HOME", ENV_GLOBAL | ENV_EXPORT); } @@ -276,8 +286,8 @@ void misc_init() { /// Make sure the PATH variable contains something. static void setup_path() { auto &vars = env_stack_t::globals(); - const auto path = vars.get(L"PATH"); - if (path.missing_or_empty()) { + const auto path = vars.get_unless_empty(L"PATH"); + if (!path) { #if defined(_CS_PATH) // _CS_PATH: colon-separated paths to find POSIX utilities std::string cspath; @@ -419,9 +429,9 @@ void env_init(const struct config_paths_t *paths, bool do_uvars, bool default_pa // Initialize termsize variables. environment_t &env_vars = vars; auto termsize = termsize_initialize_ffi(reinterpret_cast(&env_vars)); - if (vars.get(L"COLUMNS").missing_or_empty()) + if (!vars.get_unless_empty(L"COLUMNS")) vars.set_one(L"COLUMNS", ENV_GLOBAL, to_string(termsize.width)); - if (vars.get(L"LINES").missing_or_empty()) + if (!vars.get_unless_empty(L"LINES")) vars.set_one(L"LINES", ENV_GLOBAL, to_string(termsize.height)); // Set fish_bind_mode to "default". diff --git a/src/env.h b/src/env.h index cdc3e5aca..e1b1d5334 100644 --- a/src/env.h +++ b/src/env.h @@ -194,6 +194,8 @@ class environment_t { virtual std::vector get_names(env_mode_flags_t flags) const = 0; virtual ~environment_t(); + maybe_t get_unless_empty(const wcstring &key, + env_mode_flags_t mode = ENV_DEFAULT) const; /// \return a environment variable as a unique pointer, or nullptr if none. std::unique_ptr get_or_null(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const; diff --git a/src/env_dispatch.cpp b/src/env_dispatch.cpp index 3b3ab1248..ee4a3636a 100644 --- a/src/env_dispatch.cpp +++ b/src/env_dispatch.cpp @@ -136,11 +136,11 @@ void env_dispatch_init(const environment_t &vars) { /// Properly sets all timezone information. static void handle_timezone(const wchar_t *env_var_name, const environment_t &vars) { - const auto var = vars.get(env_var_name, ENV_DEFAULT); + const auto var = vars.get_unless_empty(env_var_name, ENV_DEFAULT); FLOGF(env_dispatch, L"handle_timezone() current timezone var: |%ls| => |%ls|", env_var_name, - !var ? L"MISSING" : var->as_string().c_str()); + !var ? L"MISSING/EMPTY" : var->as_string().c_str()); std::string name = wcs2zstring(env_var_name); - if (var.missing_or_empty()) { + if (!var) { unsetenv_lock(name.c_str()); } else { const std::string value = wcs2zstring(var->as_string()); @@ -288,8 +288,8 @@ static void handle_fish_use_posix_spawn_change(const environment_t &vars) { /// Allow the user to override the limit on how much data the `read` command will process. /// This is primarily for testing but could be used by users in special situations. static void handle_read_limit_change(const environment_t &vars) { - auto read_byte_limit_var = vars.get(L"fish_read_limit"); - if (!read_byte_limit_var.missing_or_empty()) { + auto read_byte_limit_var = vars.get_unless_empty(L"fish_read_limit"); + if (read_byte_limit_var) { size_t limit = fish_wcstoull(read_byte_limit_var->as_string().c_str()); if (errno) { FLOGF(warning, "Ignoring fish_read_limit since it is not valid"); @@ -302,7 +302,7 @@ static void handle_read_limit_change(const environment_t &vars) { } static void handle_fish_trace(const environment_t &vars) { - trace_set_enabled(!vars.get(L"fish_trace").missing_or_empty()); + trace_set_enabled(vars.get_unless_empty(L"fish_trace").has_value()); } /// Populate the dispatch table used by `env_dispatch_var_change()` to efficiently call the @@ -454,8 +454,8 @@ static void initialize_curses_using_fallbacks(const environment_t &vars) { const wchar_t *const fallbacks[] = {L"xterm-256color", L"xterm", L"ansi", L"dumb"}; wcstring termstr = L""; - auto term_var = vars.get(L"TERM"); - if (!term_var.missing_or_empty()) { + auto term_var = vars.get_unless_empty(L"TERM"); + if (term_var) { termstr = term_var->as_string(); } @@ -541,8 +541,8 @@ static void apply_term_hacks(const environment_t &vars) { static const wchar_t *const title_terms[] = {L"xterm", L"screen", L"tmux", L"nxterm", L"rxvt", L"alacritty", L"wezterm"}; static bool does_term_support_setting_title(const environment_t &vars) { - const auto term_var = vars.get(L"TERM"); - if (term_var.missing_or_empty()) return false; + const auto term_var = vars.get_unless_empty(L"TERM"); + if (!term_var) return false; const wcstring term_str = term_var->as_string(); const wchar_t *term = term_str.c_str(); @@ -569,8 +569,8 @@ static bool does_term_support_setting_title(const environment_t &vars) { static void init_curses(const environment_t &vars) { for (const auto &var_name : curses_variables) { std::string name = wcs2zstring(var_name); - const auto var = vars.get(var_name, ENV_EXPORT); - if (var.missing_or_empty()) { + const auto var = vars.get_unless_empty(var_name, ENV_EXPORT); + if (!var) { FLOGF(term_support, L"curses var %s missing or empty", name.c_str()); unsetenv_lock(name.c_str()); } else { @@ -583,9 +583,9 @@ static void init_curses(const environment_t &vars) { int err_ret{0}; if (setupterm(nullptr, STDOUT_FILENO, &err_ret) == ERR) { if (is_interactive_session()) { - auto term = vars.get(L"TERM"); + auto term = vars.get_unless_empty(L"TERM"); FLOGF(warning, _(L"Could not set up terminal.")); - if (term.missing_or_empty()) { + if (!term) { FLOGF(warning, _(L"TERM environment variable not set.")); } else { FLOGF(warning, _(L"TERM environment variable set to '%ls'."), @@ -619,9 +619,9 @@ static void init_locale(const environment_t &vars) { char *old_msg_locale = strdup(setlocale(LC_MESSAGES, nullptr)); for (const auto &var_name : locale_variables) { - const auto var = vars.get(var_name, ENV_EXPORT); + const auto var = vars.get_unless_empty(var_name, ENV_EXPORT); std::string name = wcs2zstring(var_name); - if (var.missing_or_empty()) { + if (!var) { FLOGF(env_locale, L"locale var %s missing or empty", name.c_str()); unsetenv_lock(name.c_str()); } else { @@ -637,8 +637,8 @@ static void init_locale(const environment_t &vars) { // A "C" locale is broken for our purposes - any wchar functions will break on it. // So we try *really really really hard* to not have one. bool fix_locale = true; - if (auto allow_c = vars.get(L"fish_allow_singlebyte_locale")) { - fix_locale = allow_c.missing_or_empty() ? true : !bool_from_string(allow_c->as_string()); + if (auto allow_c = vars.get_unless_empty(L"fish_allow_singlebyte_locale")) { + fix_locale = !bool_from_string(allow_c->as_string()); } if (fix_locale && MB_CUR_MAX == 1) { FLOGF(env_locale, L"Have singlebyte locale, trying to fix"); diff --git a/src/exec.cpp b/src/exec.cpp index ecf22caf9..0b468972e 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -1204,7 +1204,7 @@ static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, } }); - const bool split_output = !parser.vars().get(L"IFS").missing_or_empty(); + const bool split_output = parser.vars().get_unless_empty(L"IFS").has_value(); // IO buffer creation may fail (e.g. if we have too many open files to make a pipe), so this may // be null. diff --git a/src/expand.cpp b/src/expand.cpp index cdeab497e..06e98b978 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -813,8 +813,8 @@ static void expand_home_directory(wcstring &input, const environment_t &vars) { maybe_t home; if (username.empty()) { // Current users home directory. - auto home_var = vars.get(L"HOME"); - if (home_var.missing_or_empty()) { + auto home_var = vars.get_unless_empty(L"HOME"); + if (!home_var) { input.clear(); return; } diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index fad0735ed..f4f3179bd 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -5746,13 +5746,6 @@ void test_maybe() { maybe_t n = none(); do_test(!bool(n)); - maybe_t m2("abc"); - do_test(!m2.missing_or_empty()); - m2 = ""; - do_test(m2.missing_or_empty()); - m2 = none(); - do_test(m2.missing_or_empty()); - maybe_t m0 = none(); maybe_t m3("hi"); maybe_t m4 = m3; diff --git a/src/function.cpp b/src/function.cpp index dc8140222..388542e73 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -111,8 +111,8 @@ static void autoload_names(std::unordered_set &names, bool get_hidden) // TODO: justify this. auto &vars = env_stack_t::principal(); - const auto path_var = vars.get(L"fish_function_path"); - if (path_var.missing_or_empty()) return; + const auto path_var = vars.get_unless_empty(L"fish_function_path"); + if (!path_var) return; const std::vector &path_list = path_var->as_list(); diff --git a/src/highlight.cpp b/src/highlight.cpp index b719db8f5..91cdfe99b 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -308,9 +308,8 @@ static bool is_potential_cd_path(const wcstring &path, bool at_cursor, directories.push_back(working_directory); } else { // Get the CDPATH. - auto cdpath = ctx.vars.get(L"CDPATH"); - std::vector pathsv = - cdpath.missing_or_empty() ? std::vector{L"."} : cdpath->as_list(); + auto cdpath = ctx.vars.get_unless_empty(L"CDPATH"); + std::vector pathsv = !cdpath ? std::vector{L"."} : cdpath->as_list(); // The current $PWD is always valid. pathsv.push_back(L"."); @@ -344,9 +343,9 @@ rgb_color_t highlight_color_resolver_t::resolve_spec_uncached(const highlight_sp rgb_color_t result = rgb_color_t::normal(); highlight_role_t role = is_background ? highlight.background : highlight.foreground; - auto var = vars.get(get_highlight_var_name(role)); - if (var.missing_or_empty()) var = vars.get(get_highlight_var_name(get_fallback(role))); - if (var.missing_or_empty()) var = vars.get(get_highlight_var_name(highlight_role_t::normal)); + auto var = vars.get_unless_empty(get_highlight_var_name(role)); + if (!var) var = vars.get_unless_empty(get_highlight_var_name(get_fallback(role))); + if (!var) var = vars.get(get_highlight_var_name(highlight_role_t::normal)); if (var) result = parse_color(*var, is_background); // Handle modifiers. diff --git a/src/history.cpp b/src/history.cpp index 71a7e025a..7a0af2aa7 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -1583,5 +1583,5 @@ void start_private_mode(env_stack_t &vars) { } bool in_private_mode(const environment_t &vars) { - return !vars.get(L"fish_private_mode").missing_or_empty(); + return vars.get_unless_empty(L"fish_private_mode").has_value(); } diff --git a/src/input_common.cpp b/src/input_common.cpp index bd5eba595..e827b3d70 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -123,8 +123,8 @@ static readb_result_t readb(int in_fd) { // Update the wait_on_escape_ms value in response to the fish_escape_delay_ms user variable being // set. void update_wait_on_escape_ms(const environment_t& vars) { - auto escape_time_ms = vars.get(L"fish_escape_delay_ms"); - if (escape_time_ms.missing_or_empty()) { + auto escape_time_ms = vars.get_unless_empty(L"fish_escape_delay_ms"); + if (!escape_time_ms) { wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; return; } diff --git a/src/maybe.h b/src/maybe.h index ed2dcdd58..3dde986c2 100644 --- a/src/maybe.h +++ b/src/maybe.h @@ -242,13 +242,6 @@ class maybe_t : private maybe_detail::conditionally_copyable_t { const T &operator*() const { return value(); } T &operator*() { return value(); } - // Helper to replace missing_or_empty() on env_var_t. - // Uses SFINAE to only introduce this function if T has an empty() type. - template - decltype(S().empty(), bool()) missing_or_empty() const { - return !has_value() || value().empty(); - } - // Compare values for equality. bool operator==(const maybe_t &rhs) const { if (this->has_value() && rhs.has_value()) return this->value() == rhs.value(); diff --git a/src/path.cpp b/src/path.cpp index 55417f2ec..4a4b8c673 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -353,13 +353,13 @@ static base_directory_t make_base_directory(const wcstring &xdg_var, // 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()) { + const auto xdg_dir = vars.get_unless_empty(xdg_var, ENV_GLOBAL | ENV_EXPORT); + if (xdg_dir) { 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()) { + const auto home = vars.get_unless_empty(L"HOME", ENV_GLOBAL | ENV_EXPORT); + if (home) { result.path = home->as_string() + non_xdg_homepath; } }