mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-13 21:44:16 +00:00
Switch env_var to using maybe_t
This eliminates the "missing" notion of env_var_t. Instead env_get returns a maybe_t<env_var_t>, which forces callers to handle the possibility that the variable is missing.
This commit is contained in:
parent
18203a081c
commit
3d40292c00
23 changed files with 202 additions and 242 deletions
|
@ -65,15 +65,15 @@ int autoload_t::load(const wcstring &cmd, bool reload) {
|
|||
CHECK_BLOCK(0);
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
env_var_t path_var = env_get(env_var_name);
|
||||
auto path_var = env_get(env_var_name);
|
||||
|
||||
// Do we know where to look?
|
||||
if (path_var.empty()) return 0;
|
||||
if (path_var.missing_or_empty()) return 0;
|
||||
|
||||
// Check if the lookup path has changed. If so, drop all loaded files. path_var may only be
|
||||
// inspected on the main thread.
|
||||
if (path_var != this->last_path) {
|
||||
this->last_path = path_var;
|
||||
if (*path_var != this->last_path) {
|
||||
this->last_path = *path_var;
|
||||
this->last_path_tokenized.clear();
|
||||
this->last_path.to_list(this->last_path_tokenized);
|
||||
|
||||
|
@ -106,11 +106,11 @@ int autoload_t::load(const wcstring &cmd, bool reload) {
|
|||
}
|
||||
|
||||
bool autoload_t::can_load(const wcstring &cmd, const env_vars_snapshot_t &vars) {
|
||||
const env_var_t path_var = vars.get(env_var_name);
|
||||
auto path_var = vars.get(env_var_name);
|
||||
if (path_var.missing_or_empty()) return false;
|
||||
|
||||
std::vector<wcstring> path_list;
|
||||
path_var.to_list(path_list);
|
||||
path_var->to_list(path_list);
|
||||
return this->locate_file_and_maybe_load_it(cmd, false, false, path_list);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,19 +39,15 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||
if (argv[optind]) {
|
||||
dir_in = env_var_t(L"", argv[optind]); // unamed var
|
||||
} else {
|
||||
dir_in = env_get(L"HOME");
|
||||
if (dir_in.missing_or_empty()) {
|
||||
auto maybe_dir_in = env_get(L"HOME");
|
||||
if (maybe_dir_in.missing_or_empty()) {
|
||||
streams.err.append_format(_(L"%ls: Could not find home directory\n"), cmd);
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
dir_in = std::move(*maybe_dir_in);
|
||||
}
|
||||
|
||||
bool got_cd_path = false;
|
||||
if (!dir_in.missing()) {
|
||||
got_cd_path = path_get_cdpath(dir_in, &dir);
|
||||
}
|
||||
|
||||
if (!got_cd_path) {
|
||||
if (!path_get_cdpath(dir_in, &dir)) {
|
||||
if (errno == ENOTDIR) {
|
||||
streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd,
|
||||
dir_in.as_string().c_str());
|
||||
|
|
|
@ -192,9 +192,7 @@ static wcstring functions_def(const wcstring &name) {
|
|||
end = inherit_vars.end();
|
||||
it != end; ++it) {
|
||||
wcstring_list_t lst;
|
||||
if (!it->second.missing()) {
|
||||
it->second.to_list(lst);
|
||||
}
|
||||
it->second.to_list(lst);
|
||||
|
||||
// This forced tab is crummy, but we don't know what indentation style the function uses.
|
||||
append_format(out, L"\n\tset -l %ls", it->first.c_str());
|
||||
|
|
|
@ -146,17 +146,17 @@ static double *retrieve_var(const wchar_t *var_name, void *user_data) {
|
|||
UNUSED(user_data);
|
||||
static double zero_result = 0.0;
|
||||
|
||||
env_var_t var = env_get(var_name, ENV_DEFAULT);
|
||||
if (var.missing()) {
|
||||
auto var = env_get(var_name, ENV_DEFAULT);
|
||||
if (!var) {
|
||||
// We could report an error but we normally don't treat missing vars as a fatal error.
|
||||
// throw mu::ParserError(L"Var '%ls' does not exist.");
|
||||
return &zero_result;
|
||||
}
|
||||
if (var.empty()) {
|
||||
if (var->empty()) {
|
||||
return &zero_result;
|
||||
}
|
||||
|
||||
const wchar_t *first_val = var.as_list()[0].c_str();
|
||||
const wchar_t *first_val = var->as_list()[0].c_str();
|
||||
wchar_t *endptr;
|
||||
errno = 0;
|
||||
double result = wcstod(first_val, &endptr);
|
||||
|
|
|
@ -428,8 +428,8 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||
}
|
||||
|
||||
if (!opts.have_delimiter) {
|
||||
env_var_t ifs = env_get(L"IFS");
|
||||
if (!ifs.missing_or_empty()) opts.delimiter = ifs.as_string();
|
||||
auto ifs = env_get(L"IFS");
|
||||
if (!ifs.missing_or_empty()) opts.delimiter = ifs->as_string();
|
||||
}
|
||||
|
||||
if (opts.delimiter.empty()) {
|
||||
|
|
|
@ -214,8 +214,8 @@ static int validate_cmd_opts(const wchar_t *cmd, set_cmd_opts_t &opts, //!OCLIN
|
|||
static int check_global_scope_exists(const wchar_t *cmd, set_cmd_opts_t &opts, const wchar_t *dest,
|
||||
io_streams_t &streams) {
|
||||
if (opts.universal) {
|
||||
env_var_t global_dest = env_get(dest, ENV_GLOBAL);
|
||||
if (!global_dest.missing() && shell_is_interactive()) {
|
||||
auto global_dest = env_get(dest, ENV_GLOBAL);
|
||||
if (global_dest && shell_is_interactive()) {
|
||||
streams.err.append_format(BUILTIN_SET_UVAR_ERR, cmd, dest);
|
||||
}
|
||||
}
|
||||
|
@ -239,8 +239,8 @@ static int my_env_path_setup(const wchar_t *cmd, const wchar_t *key, //!OCLINT(
|
|||
// not the (missing) local value. Also don't bother to complain about relative paths, which
|
||||
// don't start with /.
|
||||
wcstring_list_t existing_values;
|
||||
const env_var_t existing_variable = env_get(key, ENV_DEFAULT);
|
||||
if (!existing_variable.missing_or_empty()) existing_variable.to_list(existing_values);
|
||||
const auto existing_variable = env_get(key, ENV_DEFAULT);
|
||||
if (!existing_variable.missing_or_empty()) existing_variable->to_list(existing_values);
|
||||
|
||||
for (size_t i = 0; i < list.size(); i++) {
|
||||
const wcstring &dir = list.at(i);
|
||||
|
@ -342,9 +342,9 @@ static int parse_index(std::vector<long> &indexes, wchar_t *src, int scope, io_s
|
|||
*p = L'\0'; // split the var name from the indexes/slices
|
||||
p++;
|
||||
|
||||
env_var_t var_str = env_get(src, scope);
|
||||
auto var_str = env_get(src, scope);
|
||||
wcstring_list_t var;
|
||||
if (!var_str.missing()) var_str.to_list(var);
|
||||
if (var_str) var_str->to_list(var);
|
||||
|
||||
int count = 0;
|
||||
|
||||
|
@ -453,10 +453,10 @@ static int builtin_set_list(const wchar_t *cmd, set_cmd_opts_t &opts, int argc,
|
|||
streams.out.append(e_key);
|
||||
|
||||
if (!names_only) {
|
||||
env_var_t var = env_get(key, compute_scope(opts));
|
||||
auto var = env_get(key, compute_scope(opts));
|
||||
if (!var.missing_or_empty()) {
|
||||
bool shorten = false;
|
||||
wcstring val = expand_escape_variable(var);
|
||||
wcstring val = expand_escape_variable(*var);
|
||||
if (opts.shorten_ok && val.length() > 64) {
|
||||
shorten = true;
|
||||
val.resize(60);
|
||||
|
@ -495,15 +495,14 @@ static int builtin_set_query(const wchar_t *cmd, set_cmd_opts_t &opts, int argc,
|
|||
|
||||
if (idx_count) {
|
||||
wcstring_list_t result;
|
||||
env_var_t dest_str = env_get(dest, scope);
|
||||
if (!dest_str.missing()) dest_str.to_list(result);
|
||||
auto dest_str = env_get(dest, scope);
|
||||
if (dest_str) dest_str->to_list(result);
|
||||
|
||||
for (auto idx : indexes) {
|
||||
if (idx < 1 || (size_t)idx > result.size()) retval++;
|
||||
}
|
||||
} else {
|
||||
env_var_t var = env_get(arg, scope);
|
||||
if (var.missing()) retval++;
|
||||
if (! env_get(arg, scope)) retval++;
|
||||
}
|
||||
|
||||
free(dest);
|
||||
|
@ -533,14 +532,15 @@ static void show_scope(const wchar_t *var_name, int scope, io_streams_t &streams
|
|||
}
|
||||
}
|
||||
|
||||
const env_var_t var = env_get(var_name, scope);
|
||||
if (var.missing()) {
|
||||
const auto var = env_get(var_name, scope);
|
||||
if (!var) {
|
||||
streams.out.append_format(_(L"$%ls: not set in %ls scope\n"), var_name, scope_name);
|
||||
return;
|
||||
}
|
||||
|
||||
const wchar_t *exportv = var.exportv ? _(L"exported") : _(L"unexported");
|
||||
const wcstring_list_t &vals = var.as_list();
|
||||
|
||||
const wchar_t *exportv = var->exportv ? _(L"exported") : _(L"unexported");
|
||||
wcstring_list_t vals = var->as_list();
|
||||
streams.out.append_format(_(L"$%ls: set in %ls scope, %ls, with %d elements\n"), var_name,
|
||||
scope_name, exportv, vals.size());
|
||||
for (size_t i = 0; i < vals.size(); i++) {
|
||||
|
@ -625,10 +625,10 @@ static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc,
|
|||
if (idx_count == 0) { // unset the var
|
||||
retval = env_remove(dest, scope);
|
||||
} else { // remove just the specified indexes of the var
|
||||
const env_var_t dest_var = env_get(dest, scope);
|
||||
if (dest_var.missing()) return STATUS_CMD_ERROR;
|
||||
const auto dest_var = env_get(dest, scope);
|
||||
if (!dest_var) return STATUS_CMD_ERROR;
|
||||
wcstring_list_t result;
|
||||
dest_var.to_list(result);
|
||||
dest_var->to_list(result);
|
||||
erase_values(result, indexes);
|
||||
retval = my_env_set(cmd, dest, scope, result, streams);
|
||||
}
|
||||
|
@ -650,9 +650,9 @@ static int set_var_array(const wchar_t *cmd, set_cmd_opts_t &opts, const wchar_t
|
|||
for (int i = 0; i < argc; i++) new_values.push_back(argv[i]);
|
||||
}
|
||||
|
||||
env_var_t var_str = env_get(varname, ENV_DEFAULT);
|
||||
auto var_str = env_get(varname, ENV_DEFAULT);
|
||||
wcstring_list_t var_array;
|
||||
if (!var_str.missing()) var_str.to_list(var_array);
|
||||
if (var_str) var_str->to_list(var_array);
|
||||
new_values.insert(new_values.end(), var_array.begin(), var_array.end());
|
||||
|
||||
if (opts.append) {
|
||||
|
@ -683,8 +683,8 @@ static int set_var_slices(const wchar_t *cmd, set_cmd_opts_t &opts, const wchar_
|
|||
}
|
||||
|
||||
int scope = compute_scope(opts); // calculate the variable scope based on the provided options
|
||||
const env_var_t var_str = env_get(varname, scope);
|
||||
if (!var_str.missing()) var_str.to_list(new_values);
|
||||
const auto var_str = env_get(varname, scope);
|
||||
if (var_str) var_str->to_list(new_values);
|
||||
|
||||
// Slice indexes have been calculated, do the actual work.
|
||||
wcstring_list_t result;
|
||||
|
|
|
@ -1553,13 +1553,13 @@ static void validate_new_termsize(struct winsize *new_termsize) {
|
|||
}
|
||||
#endif
|
||||
// Fallback to the environment vars.
|
||||
env_var_t col_var = env_get(L"COLUMNS");
|
||||
env_var_t row_var = env_get(L"LINES");
|
||||
maybe_t<env_var_t> col_var = env_get(L"COLUMNS");
|
||||
maybe_t<env_var_t> row_var = env_get(L"LINES");
|
||||
if (!col_var.missing_or_empty() && !row_var.missing_or_empty()) {
|
||||
// Both vars have to have valid values.
|
||||
int col = fish_wcstoi(col_var.as_string().c_str());
|
||||
int col = fish_wcstoi(col_var->as_string().c_str());
|
||||
bool col_ok = errno == 0 && col > 0 && col <= USHRT_MAX;
|
||||
int row = fish_wcstoi(row_var.as_string().c_str());
|
||||
int row = fish_wcstoi(row_var->as_string().c_str());
|
||||
bool row_ok = errno == 0 && row > 0 && row <= USHRT_MAX;
|
||||
if (col_ok && row_ok) {
|
||||
new_termsize->ws_col = col;
|
||||
|
@ -1582,11 +1582,11 @@ static void validate_new_termsize(struct winsize *new_termsize) {
|
|||
static void export_new_termsize(struct winsize *new_termsize) {
|
||||
wchar_t buf[64];
|
||||
|
||||
env_var_t cols = env_get(L"COLUMNS", ENV_EXPORT);
|
||||
auto cols = env_get(L"COLUMNS", ENV_EXPORT);
|
||||
swprintf(buf, 64, L"%d", (int)new_termsize->ws_col);
|
||||
env_set_one(L"COLUMNS", ENV_GLOBAL | (cols.missing_or_empty() ? ENV_DEFAULT : ENV_EXPORT), buf);
|
||||
|
||||
env_var_t lines = env_get(L"LINES", ENV_EXPORT);
|
||||
auto lines = env_get(L"LINES", ENV_EXPORT);
|
||||
swprintf(buf, 64, L"%d", (int)new_termsize->ws_row);
|
||||
env_set_one(L"LINES", ENV_GLOBAL | (lines.missing_or_empty() ? ENV_DEFAULT : ENV_EXPORT), buf);
|
||||
|
||||
|
|
|
@ -1108,10 +1108,10 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) {
|
|||
wcstring desc;
|
||||
if (this->wants_descriptions()) {
|
||||
// Can't use this->vars here, it could be any variable.
|
||||
env_var_t var = env_get(env_name);
|
||||
if (var.missing()) continue;
|
||||
auto var = env_get(env_name);
|
||||
if (!var) continue;
|
||||
|
||||
wcstring value = expand_escape_variable(var);
|
||||
wcstring value = expand_escape_variable(*var);
|
||||
if (this->type() != COMPLETE_AUTOSUGGEST) {
|
||||
desc = format_string(COMPLETE_VAR_DESC_VAL, value.c_str());
|
||||
}
|
||||
|
|
138
src/env.cpp
138
src/env.cpp
|
@ -110,15 +110,6 @@ static const wcstring_list_t colon_delimited_variable({L"PATH", L"MANPATH", L"CD
|
|||
static void init_locale();
|
||||
static void init_curses();
|
||||
|
||||
/// Construct a missing var object
|
||||
env_var_t create_missing_var() {
|
||||
env_var_t var;
|
||||
var.set_missing();
|
||||
return var;
|
||||
}
|
||||
|
||||
env_var_t missing_var = create_missing_var();
|
||||
|
||||
/// This is used to convert a serialized env_var_t back into a list. It is used when reading legacy
|
||||
/// (fish 2.x) encoded vars stored in the universal variable file and the environment.
|
||||
static void tokenize_variable_array(const wcstring &val, wcstring_list_t &out) {
|
||||
|
@ -165,8 +156,7 @@ class env_node_t {
|
|||
/// Pointer to next level.
|
||||
std::unique_ptr<env_node_t> next;
|
||||
|
||||
/// Returns the given entry if present else env_var_t::missing_var.
|
||||
const env_var_t find_entry(const wcstring &key);
|
||||
maybe_t<env_var_t> find_entry(const wcstring &key);
|
||||
};
|
||||
|
||||
class variable_entry_t {
|
||||
|
@ -339,10 +329,10 @@ static bool is_electric(const wcstring &key) {
|
|||
return env_electric.find(key) != env_electric.end();
|
||||
}
|
||||
|
||||
const env_var_t env_node_t::find_entry(const wcstring &key) {
|
||||
maybe_t<env_var_t> env_node_t::find_entry(const wcstring &key) {
|
||||
var_table_t::const_iterator entry = env.find(key);
|
||||
if (entry != env.end()) return entry->second;
|
||||
return missing_var;
|
||||
return none();
|
||||
}
|
||||
|
||||
/// Return the current umask value.
|
||||
|
@ -356,14 +346,14 @@ static mode_t get_umask() {
|
|||
/// Properly sets all timezone information.
|
||||
static void handle_timezone(const wchar_t *env_var_name) {
|
||||
// const env_var_t var = env_get(env_var_name, ENV_EXPORT);
|
||||
const env_var_t var = env_get(env_var_name, ENV_DEFAULT);
|
||||
const auto var = env_get(env_var_name, ENV_DEFAULT);
|
||||
debug(2, L"handle_timezone() current timezone var: |%ls| => |%ls|", env_var_name,
|
||||
var.missing() ? L"MISSING" : var.as_string().c_str());
|
||||
!var ? L"MISSING" : var->as_string().c_str());
|
||||
const std::string &name = wcs2string(env_var_name);
|
||||
if (var.missing_or_empty()) {
|
||||
unsetenv(name.c_str());
|
||||
} else {
|
||||
const std::string &value = wcs2string(var.as_string());
|
||||
const std::string value = wcs2string(var->as_string());
|
||||
setenv(name.c_str(), value.c_str(), 1);
|
||||
}
|
||||
tzset();
|
||||
|
@ -376,13 +366,13 @@ static void fix_colon_delimited_var(const wcstring &var_name) {
|
|||
// While we auto split/join MANPATH we do not want to replace empty elements with "." (#4158).
|
||||
if (var_name == L"MANPATH") return;
|
||||
|
||||
const env_var_t paths = env_get(var_name);
|
||||
const auto paths = env_get(var_name);
|
||||
if (paths.missing_or_empty()) return;
|
||||
|
||||
bool modified = false;
|
||||
wcstring_list_t pathsv;
|
||||
wcstring_list_t new_pathsv;
|
||||
paths.to_list(pathsv);
|
||||
paths->to_list(pathsv);
|
||||
for (auto next_path : pathsv) {
|
||||
if (next_path.empty()) {
|
||||
next_path = L".";
|
||||
|
@ -408,13 +398,13 @@ static void init_locale() {
|
|||
char *old_msg_locale = strdup(setlocale(LC_MESSAGES, NULL));
|
||||
|
||||
for (auto var_name : locale_variables) {
|
||||
const env_var_t var = env_get(var_name, ENV_EXPORT);
|
||||
const auto var = env_get(var_name, ENV_EXPORT);
|
||||
const std::string &name = wcs2string(var_name);
|
||||
if (var.missing_or_empty()) {
|
||||
debug(2, L"locale var %s missing or empty", name.c_str());
|
||||
unsetenv(name.c_str());
|
||||
} else {
|
||||
const std::string &value = wcs2string(var.as_string());
|
||||
const std::string value = wcs2string(var->as_string());
|
||||
debug(2, L"locale var %s='%s'", name.c_str(), value.c_str());
|
||||
setenv(name.c_str(), value.c_str(), 1);
|
||||
}
|
||||
|
@ -454,12 +444,12 @@ bool term_supports_setting_title() { return can_set_term_title; }
|
|||
/// don't. Since we can't see the underlying terminal below screen there is no way to fix this.
|
||||
static const wcstring_list_t title_terms({L"xterm", L"screen", L"tmux", L"nxterm", L"rxvt"});
|
||||
static bool does_term_support_setting_title() {
|
||||
const env_var_t term_var = env_get(L"TERM");
|
||||
const auto term_var = env_get(L"TERM");
|
||||
if (term_var.missing_or_empty()) return false;
|
||||
|
||||
const wcstring term_str = term_var.as_string();
|
||||
const wcstring term_str = term_var->as_string();
|
||||
const wchar_t *term = term_str.c_str();
|
||||
bool recognized = contains(title_terms, term_var.as_string());
|
||||
bool recognized = contains(title_terms, term_var->as_string());
|
||||
if (!recognized) recognized = !wcsncmp(term, L"xterm-", wcslen(L"xterm-"));
|
||||
if (!recognized) recognized = !wcsncmp(term, L"screen-", wcslen(L"screen-"));
|
||||
if (!recognized) recognized = !wcsncmp(term, L"tmux-", wcslen(L"tmux-"));
|
||||
|
@ -479,12 +469,12 @@ static bool does_term_support_setting_title() {
|
|||
static void update_fish_color_support() {
|
||||
// Detect or infer term256 support. If fish_term256 is set, we respect it;
|
||||
// otherwise infer it from the TERM variable or use terminfo.
|
||||
env_var_t fish_term256 = env_get(L"fish_term256");
|
||||
env_var_t term_var = env_get(L"TERM");
|
||||
wcstring term = term_var.missing_or_empty() ? L"" : term_var.as_string();
|
||||
auto fish_term256 = env_get(L"fish_term256");
|
||||
auto term_var = env_get(L"TERM");
|
||||
wcstring term = term_var.missing_or_empty() ? L"" : term_var->as_string();
|
||||
bool support_term256 = false; // default to no support
|
||||
if (!fish_term256.missing_or_empty()) {
|
||||
support_term256 = from_string<bool>(fish_term256.as_string());
|
||||
support_term256 = from_string<bool>(fish_term256->as_string());
|
||||
debug(2, L"256 color support determined by 'fish_term256'");
|
||||
} else if (term.find(L"256color") != wcstring::npos) {
|
||||
// TERM=*256color*: Explicitly supported.
|
||||
|
@ -492,12 +482,12 @@ static void update_fish_color_support() {
|
|||
debug(2, L"256 color support enabled for '256color' in TERM");
|
||||
} else if (term.find(L"xterm") != wcstring::npos) {
|
||||
// Assume that all xterms are 256, except for OS X SnowLeopard
|
||||
const env_var_t prog_var = env_get(L"TERM_PROGRAM");
|
||||
const env_var_t progver_var = env_get(L"TERM_PROGRAM_VERSION");
|
||||
wcstring term_program = prog_var.missing_or_empty() ? L"" : prog_var.as_string();
|
||||
const auto prog_var = env_get(L"TERM_PROGRAM");
|
||||
const auto progver_var = env_get(L"TERM_PROGRAM_VERSION");
|
||||
wcstring term_program = prog_var.missing_or_empty() ? L"" : prog_var->as_string();
|
||||
if (term_program == L"Apple_Terminal" && !progver_var.missing_or_empty()) {
|
||||
// OS X Lion is version 300+, it has 256 color support
|
||||
if (strtod(wcs2str(progver_var.as_string()), NULL) > 300) {
|
||||
if (strtod(wcs2str(progver_var->as_string()), NULL) > 300) {
|
||||
support_term256 = true;
|
||||
debug(2, L"256 color support enabled for TERM=xterm + modern Terminal.app");
|
||||
}
|
||||
|
@ -513,10 +503,10 @@ static void update_fish_color_support() {
|
|||
debug(2, L"256 color support not enabled (yet)");
|
||||
}
|
||||
|
||||
env_var_t fish_term24bit = env_get(L"fish_term24bit");
|
||||
auto fish_term24bit = env_get(L"fish_term24bit");
|
||||
bool support_term24bit;
|
||||
if (!fish_term24bit.missing_or_empty()) {
|
||||
support_term24bit = from_string<bool>(fish_term24bit.as_string());
|
||||
support_term24bit = from_string<bool>(fish_term24bit->as_string());
|
||||
debug(2, L"'fish_term24bit' preference: 24-bit color %s",
|
||||
support_term24bit ? L"enabled" : L"disabled");
|
||||
} else {
|
||||
|
@ -536,10 +526,10 @@ static void update_fish_color_support() {
|
|||
static bool initialize_curses_using_fallback(const char *term) {
|
||||
// If $TERM is already set to the fallback name we're about to use there isn't any point in
|
||||
// seeing if the fallback name can be used.
|
||||
env_var_t term_var = env_get(L"TERM");
|
||||
auto term_var = env_get(L"TERM");
|
||||
if (term_var.missing_or_empty()) return false;
|
||||
|
||||
const char *term_env = wcs2str(term_var.as_string());
|
||||
const char *term_env = wcs2str(term_var->as_string());
|
||||
if (!strcmp(term_env, DEFAULT_TERM1) || !strcmp(term_env, DEFAULT_TERM2)) return false;
|
||||
|
||||
if (is_interactive_session) debug(1, _(L"Using fallback terminal type '%s'."), term);
|
||||
|
@ -563,13 +553,13 @@ static void init_path_vars() {
|
|||
/// Initialize the curses subsystem.
|
||||
static void init_curses() {
|
||||
for (auto var_name : curses_variables) {
|
||||
const env_var_t var = env_get(var_name, ENV_EXPORT);
|
||||
const auto var = env_get(var_name, ENV_EXPORT);
|
||||
const std::string &name = wcs2string(var_name);
|
||||
if (var.missing_or_empty()) {
|
||||
debug(2, L"curses var %s missing or empty", name.c_str());
|
||||
unsetenv(name.c_str());
|
||||
} else {
|
||||
const std::string &value = wcs2string(var.as_string());
|
||||
const std::string &value = wcs2string(var->as_string());
|
||||
debug(2, L"curses var %s='%s'", name.c_str(), value.c_str());
|
||||
setenv(name.c_str(), value.c_str(), 1);
|
||||
}
|
||||
|
@ -577,13 +567,13 @@ static void init_curses() {
|
|||
|
||||
int err_ret;
|
||||
if (setupterm(NULL, STDOUT_FILENO, &err_ret) == ERR) {
|
||||
env_var_t term = env_get(L"TERM");
|
||||
auto term = env_get(L"TERM");
|
||||
if (is_interactive_session) {
|
||||
debug(1, _(L"Could not set up terminal."));
|
||||
if (term.missing_or_empty()) {
|
||||
debug(1, _(L"TERM environment variable not set."));
|
||||
} else {
|
||||
debug(1, _(L"TERM environment variable set to '%ls'."), term.as_string().c_str());
|
||||
debug(1, _(L"TERM environment variable set to '%ls'."), term->as_string().c_str());
|
||||
debug(1, _(L"Check that this terminal type is supported on this system."));
|
||||
}
|
||||
}
|
||||
|
@ -656,7 +646,7 @@ static void universal_callback(fish_message_type_t type, const wchar_t *name) {
|
|||
|
||||
/// Make sure the PATH variable contains something.
|
||||
static void setup_path() {
|
||||
const env_var_t path = env_get(L"PATH");
|
||||
const auto path = env_get(L"PATH");
|
||||
if (path.missing_or_empty()) {
|
||||
wcstring_list_t value({L"/usr/bin", L"/bin"});
|
||||
env_set(L"PATH", ENV_GLOBAL | ENV_EXPORT, value);
|
||||
|
@ -667,10 +657,10 @@ static void setup_path() {
|
|||
/// defaults. They will be updated later by the `get_current_winsize()` function if they need to be
|
||||
/// adjusted.
|
||||
static void env_set_termsize() {
|
||||
env_var_t cols = env_get(L"COLUMNS");
|
||||
auto cols = env_get(L"COLUMNS");
|
||||
if (cols.missing_or_empty()) env_set_one(L"COLUMNS", ENV_GLOBAL, DFLT_TERM_COL_STR);
|
||||
|
||||
env_var_t rows = env_get(L"LINES");
|
||||
auto rows = env_get(L"LINES");
|
||||
if (rows.missing_or_empty()) env_set_one(L"LINES", ENV_GLOBAL, DFLT_TERM_ROW_STR);
|
||||
}
|
||||
|
||||
|
@ -688,9 +678,9 @@ bool env_set_pwd() {
|
|||
/// 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.
|
||||
void env_set_read_limit() {
|
||||
env_var_t read_byte_limit_var = env_get(L"FISH_READ_BYTE_LIMIT");
|
||||
auto read_byte_limit_var = env_get(L"FISH_READ_BYTE_LIMIT");
|
||||
if (!read_byte_limit_var.missing_or_empty()) {
|
||||
size_t limit = fish_wcstoull(read_byte_limit_var.as_string().c_str());
|
||||
size_t limit = fish_wcstoull(read_byte_limit_var->as_string().c_str());
|
||||
if (errno) {
|
||||
debug(1, "Ignoring FISH_READ_BYTE_LIMIT since it is not valid");
|
||||
} else {
|
||||
|
@ -700,11 +690,11 @@ void env_set_read_limit() {
|
|||
}
|
||||
|
||||
wcstring env_get_pwd_slash(void) {
|
||||
env_var_t pwd_var = env_get(L"PWD");
|
||||
auto pwd_var = env_get(L"PWD");
|
||||
if (pwd_var.missing_or_empty()) {
|
||||
return L"";
|
||||
}
|
||||
wcstring pwd = pwd_var.as_string();
|
||||
wcstring pwd = pwd_var->as_string();
|
||||
if (!string_suffixes_string(L"/", pwd)) {
|
||||
pwd.push_back(L'/');
|
||||
}
|
||||
|
@ -937,12 +927,12 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
|
|||
env_set_one(L"FISH_VERSION", ENV_GLOBAL, version);
|
||||
|
||||
// Set up SHLVL variable.
|
||||
const env_var_t shlvl_var = env_get(L"SHLVL");
|
||||
const auto shlvl_var = env_get(L"SHLVL");
|
||||
wcstring nshlvl_str = L"1";
|
||||
if (!shlvl_var.missing_or_empty()) {
|
||||
const wchar_t *end;
|
||||
// TODO: Figure out how to handle invalid numbers better. Shouldn't we issue a diagnostic?
|
||||
long shlvl_i = fish_wcstol(shlvl_var.as_string().c_str(), &end);
|
||||
long shlvl_i = fish_wcstol(shlvl_var->as_string().c_str(), &end);
|
||||
if (!errno && shlvl_i >= 0) {
|
||||
nshlvl_str = to_string<long>(shlvl_i + 1);
|
||||
}
|
||||
|
@ -956,9 +946,9 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
|
|||
// Since that is an explicit choice, we should allow it to enable e.g.
|
||||
// env HOME=(mktemp -d) su --preserve-environment fish
|
||||
if (env_get(L"HOME").missing_or_empty()) {
|
||||
env_var_t user_var = env_get(L"USER");
|
||||
auto user_var = env_get(L"USER");
|
||||
if (!user_var.missing_or_empty()) {
|
||||
char *unam_narrow = wcs2str(user_var.as_string());
|
||||
char *unam_narrow = wcs2str(user_var->as_string());
|
||||
struct passwd userinfo;
|
||||
struct passwd *result;
|
||||
char buf[8192];
|
||||
|
@ -968,7 +958,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
|
|||
setup_user(true);
|
||||
user_var = env_get(L"USER");
|
||||
if (!user_var.missing_or_empty()) {
|
||||
unam_narrow = wcs2str(user_var.as_string());
|
||||
unam_narrow = wcs2str(user_var->as_string());
|
||||
retval = getpwnam_r(unam_narrow, &userinfo, buf, sizeof(buf), &result);
|
||||
}
|
||||
}
|
||||
|
@ -992,9 +982,9 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
|
|||
env_set_read_limit(); // initialize the read_byte_limit
|
||||
|
||||
// Set g_use_posix_spawn. Default to true.
|
||||
env_var_t use_posix_spawn = env_get(L"fish_use_posix_spawn");
|
||||
auto use_posix_spawn = env_get(L"fish_use_posix_spawn");
|
||||
g_use_posix_spawn =
|
||||
use_posix_spawn.missing_or_empty() ? true : from_string<bool>(use_posix_spawn.as_string());
|
||||
use_posix_spawn.missing_or_empty() ? true : from_string<bool>(use_posix_spawn->as_string());
|
||||
|
||||
// Set fish_bind_mode to "default".
|
||||
env_set_one(FISH_BIND_MODE_VAR, ENV_GLOBAL, DEFAULT_BIND_MODE);
|
||||
|
@ -1023,8 +1013,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
|
|||
static env_node_t *env_get_node(const wcstring &key) {
|
||||
env_node_t *env = vars_stack().top.get();
|
||||
while (env != NULL) {
|
||||
env_var_t var = env->find_entry(key);
|
||||
if (!var.missing()) break;
|
||||
if (env->find_entry(key)) break;
|
||||
env = vars_stack().next_scope_to_search(env);
|
||||
}
|
||||
return env;
|
||||
|
@ -1140,7 +1129,7 @@ static int env_set_internal(const wcstring &key, env_mode_flags_t var_mode, wcst
|
|||
env_universal_barrier();
|
||||
}
|
||||
|
||||
if (uvars() && !uvars()->get(key).missing()) {
|
||||
if (uvars() && uvars()->get(key)) {
|
||||
bool exportv;
|
||||
if (var_mode & ENV_EXPORT) {
|
||||
exportv = true;
|
||||
|
@ -1296,7 +1285,6 @@ const wcstring_list_t &env_var_t::as_list() const { return vals; }
|
|||
/// Return a string representation of the var. At the present time this uses the legacy 2.x
|
||||
/// encoding.
|
||||
wcstring env_var_t::as_string(void) const {
|
||||
assert(!this->is_missing);
|
||||
if (this->vals.empty()) return wcstring(ENV_NULL);
|
||||
|
||||
wchar_t sep = variable_is_colon_delimited_var(this->name) ? L':' : ARRAY_SEP;
|
||||
|
@ -1310,11 +1298,10 @@ wcstring env_var_t::as_string(void) const {
|
|||
}
|
||||
|
||||
void env_var_t::to_list(wcstring_list_t &out) const {
|
||||
assert(!is_missing);
|
||||
out = vals;
|
||||
}
|
||||
|
||||
env_var_t env_get(const wcstring &key, env_mode_flags_t mode) {
|
||||
maybe_t<env_var_t> env_get(const wcstring &key, env_mode_flags_t mode) {
|
||||
const bool has_scope = mode & (ENV_LOCAL | ENV_GLOBAL | ENV_UNIVERSAL);
|
||||
const bool search_local = !has_scope || (mode & ENV_LOCAL);
|
||||
const bool search_global = !has_scope || (mode & ENV_GLOBAL);
|
||||
|
@ -1326,12 +1313,12 @@ env_var_t env_get(const wcstring &key, env_mode_flags_t mode) {
|
|||
// Make the assumption that electric keys can't be shadowed elsewhere, since we currently block
|
||||
// that in env_set().
|
||||
if (is_electric(key)) {
|
||||
if (!search_global) return missing_var;
|
||||
if (!search_global) return none();
|
||||
if (key == L"history") {
|
||||
// Big hack. We only allow getting the history on the main thread. Note that history_t
|
||||
// may ask for an environment variable, so don't take the lock here (we don't need it).
|
||||
if (!is_main_thread()) {
|
||||
return missing_var;
|
||||
return none();
|
||||
}
|
||||
|
||||
history_t *history = reader_get_history();
|
||||
|
@ -1362,7 +1349,7 @@ env_var_t env_get(const wcstring &key, env_mode_flags_t mode) {
|
|||
var_table_t::const_iterator result = env->env.find(key);
|
||||
if (result != env->env.end()) {
|
||||
const env_var_t var = result->second;
|
||||
if (!var.missing() && (var.exportv ? search_exported : search_unexported)) {
|
||||
if (var.exportv ? search_exported : search_unexported) {
|
||||
return var;
|
||||
}
|
||||
}
|
||||
|
@ -1370,7 +1357,7 @@ env_var_t env_get(const wcstring &key, env_mode_flags_t mode) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!search_universal) return missing_var;
|
||||
if (!search_universal) return none();
|
||||
|
||||
// Another hack. Only do a universal barrier on the main thread (since it can change variable
|
||||
// values). Make sure we do this outside the env_lock because it may itself call `env_get()`.
|
||||
|
@ -1382,13 +1369,13 @@ env_var_t env_get(const wcstring &key, env_mode_flags_t mode) {
|
|||
// Okay, we couldn't find a local or global var given the requirements. If there is a matching
|
||||
// universal var return that.
|
||||
if (uvars()) {
|
||||
env_var_t var = uvars()->get(key);
|
||||
if (!var.missing() && (uvars()->get_export(key) ? search_exported : search_unexported)) {
|
||||
auto var = uvars()->get(key);
|
||||
if (var && (uvars()->get_export(key) ? search_exported : search_unexported)) {
|
||||
return var;
|
||||
}
|
||||
}
|
||||
|
||||
return missing_var;
|
||||
return none();
|
||||
}
|
||||
|
||||
/// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported
|
||||
|
@ -1482,7 +1469,7 @@ static void get_exported(const env_node_t *n, var_table_t &h) {
|
|||
const wcstring &key = iter->first;
|
||||
const env_var_t var = iter->second;
|
||||
|
||||
if (!var.missing() && var.exportv) {
|
||||
if (var.exportv) {
|
||||
// Export the variable. Don't use std::map::insert here, since we need to overwrite
|
||||
// existing values from previous scopes.
|
||||
h[key] = var;
|
||||
|
@ -1534,12 +1521,12 @@ void var_stack_t::update_export_array_if_necessary() {
|
|||
const wcstring_list_t uni = uvars()->get_names(true, false);
|
||||
for (size_t i = 0; i < uni.size(); i++) {
|
||||
const wcstring &key = uni.at(i);
|
||||
const env_var_t var = uvars()->get(key);
|
||||
auto var = uvars()->get(key);
|
||||
|
||||
if (!var.missing() && !var.empty()) {
|
||||
if (!var.missing_or_empty()) {
|
||||
// Note that std::map::insert does NOT overwrite a value already in the map,
|
||||
// which we depend on here.
|
||||
vals.insert(std::pair<wcstring, env_var_t>(key, var));
|
||||
vals.insert(std::pair<wcstring, env_var_t>(key, *var));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1575,9 +1562,9 @@ env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t *const *keys) {
|
|||
wcstring key;
|
||||
for (size_t i = 0; keys[i]; i++) {
|
||||
key.assign(keys[i]);
|
||||
const env_var_t var = env_get(key);
|
||||
if (!var.missing()) {
|
||||
vars[key] = var;
|
||||
const auto var = env_get(key);
|
||||
if (var) {
|
||||
vars[key] = *var;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1591,13 +1578,14 @@ const env_vars_snapshot_t &env_vars_snapshot_t::current() { return sCurrentSnaps
|
|||
|
||||
bool env_vars_snapshot_t::is_current() const { return this == &sCurrentSnapshot; }
|
||||
|
||||
env_var_t env_vars_snapshot_t::get(const wcstring &key) const {
|
||||
maybe_t<env_var_t> env_vars_snapshot_t::get(const wcstring &key) const {
|
||||
// If we represent the current state, bounce to env_get.
|
||||
if (this->is_current()) {
|
||||
return env_get(key);
|
||||
}
|
||||
auto iter = vars.find(key);
|
||||
return iter == vars.end() ? missing_var : env_var_t(iter->second);
|
||||
if (iter == vars.end()) return none();
|
||||
return env_var_t(iter->second);
|
||||
}
|
||||
|
||||
const wchar_t *const env_vars_snapshot_t::highlighting_keys[] = {L"PATH", L"CDPATH",
|
||||
|
|
35
src/env.h
35
src/env.h
|
@ -11,6 +11,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
#include "maybe.h"
|
||||
|
||||
extern size_t read_byte_limit;
|
||||
extern bool curses_initialized;
|
||||
|
@ -71,40 +72,31 @@ class env_var_t {
|
|||
private:
|
||||
wcstring name; // name of the var
|
||||
wcstring_list_t vals; // list of values assigned to the var
|
||||
bool is_missing; // true if the var is not set in the requested scope
|
||||
|
||||
public:
|
||||
bool exportv; // whether the variable should be exported
|
||||
|
||||
void set_missing(void) { is_missing = true; }
|
||||
|
||||
// Constructors.
|
||||
env_var_t(const env_var_t &v)
|
||||
: name(v.name), vals(v.vals), is_missing(v.is_missing), exportv(v.exportv) {}
|
||||
env_var_t(const env_var_t &v) : name(v.name), vals(v.vals), exportv(v.exportv) {}
|
||||
env_var_t(const wcstring &our_name, const wcstring_list_t &l)
|
||||
: name(our_name), vals(l), is_missing(false), exportv(false) {}
|
||||
: name(our_name), vals(l), exportv(false) {}
|
||||
env_var_t(const wcstring &our_name, const wcstring &s)
|
||||
: name(our_name),
|
||||
vals({
|
||||
s,
|
||||
}),
|
||||
is_missing(false),
|
||||
exportv(false) {}
|
||||
env_var_t(const wcstring &our_name, const wchar_t *s)
|
||||
: name(our_name),
|
||||
vals({
|
||||
wcstring(s),
|
||||
}),
|
||||
is_missing(false),
|
||||
exportv(false) {}
|
||||
env_var_t() : name(), vals(), is_missing(false), exportv(false) {}
|
||||
env_var_t() : name(), vals(), exportv(false) {}
|
||||
|
||||
bool empty(void) const { return vals.empty() || (vals.size() == 1 && vals[0].empty()); };
|
||||
bool missing(void) const { return is_missing; }
|
||||
bool missing_or_empty(void) const { return missing() || empty(); }
|
||||
|
||||
bool matches_string(const wcstring &str) {
|
||||
if (is_missing) return false;
|
||||
if (vals.size() > 1) return false;
|
||||
return vals[0] == str;
|
||||
}
|
||||
|
@ -120,35 +112,28 @@ class env_var_t {
|
|||
env_var_t &operator=(const env_var_t &var) {
|
||||
this->vals = var.vals;
|
||||
this->exportv = var.exportv;
|
||||
this->is_missing = var.is_missing;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Compare a simple string to the var. Returns true iff the var is not missing, has a single
|
||||
/// Compare a simple string to the var. Returns true iff the var has a single
|
||||
/// value and that value matches the string being compared to.
|
||||
bool operator==(const wcstring &str) const {
|
||||
if (is_missing) return false;
|
||||
if (vals.size() > 1) return false;
|
||||
return vals[0] == str;
|
||||
}
|
||||
|
||||
bool operator==(const env_var_t &var) const {
|
||||
return this->is_missing == var.is_missing && vals == var.vals;
|
||||
}
|
||||
bool operator==(const env_var_t &var) const { return vals == var.vals; }
|
||||
|
||||
bool operator==(const wcstring_list_t &values) const { return !is_missing && vals == values; }
|
||||
bool operator==(const wcstring_list_t &values) const { return vals == values; }
|
||||
|
||||
bool operator!=(const env_var_t &var) const { return vals != var.vals; }
|
||||
};
|
||||
|
||||
env_var_t create_missing_var();
|
||||
extern env_var_t missing_var;
|
||||
|
||||
/// This is used to convert a serialized env_var_t back into a list.
|
||||
wcstring_list_t decode_serialized(const wcstring &s);
|
||||
|
||||
/// Gets the variable with the specified name, or env_var_t::missing_var if it does not exist.
|
||||
env_var_t env_get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT);
|
||||
/// Gets the variable with the specified name, or none() if it does not exist.
|
||||
maybe_t<env_var_t> env_get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT);
|
||||
|
||||
/// Sets the variable with the specified name to the given values.
|
||||
int env_set(const wcstring &key, env_mode_flags_t mode, wcstring_list_t vals);
|
||||
|
@ -207,7 +192,7 @@ class env_vars_snapshot_t {
|
|||
env_vars_snapshot_t(const wchar_t *const *keys);
|
||||
env_vars_snapshot_t();
|
||||
|
||||
env_var_t get(const wcstring &key) const;
|
||||
maybe_t<env_var_t> get(const wcstring &key) const;
|
||||
|
||||
// Returns the fake snapshot representing the live variables array.
|
||||
static const env_vars_snapshot_t ¤t();
|
||||
|
|
|
@ -265,10 +265,10 @@ static bool append_file_entry(fish_message_type_t type, const wcstring &key_in,
|
|||
env_universal_t::env_universal_t(const wcstring &path)
|
||||
: explicit_vars_path(path), tried_renaming(false), last_read_file(kInvalidFileID) {}
|
||||
|
||||
env_var_t env_universal_t::get(const wcstring &name) const {
|
||||
maybe_t<env_var_t> env_universal_t::get(const wcstring &name) const {
|
||||
var_table_t::const_iterator where = vars.find(name);
|
||||
if (where != vars.end()) return where->second;
|
||||
return missing_var;
|
||||
return none();
|
||||
}
|
||||
|
||||
bool env_universal_t::get_export(const wcstring &name) const {
|
||||
|
|
|
@ -71,7 +71,7 @@ class env_universal_t {
|
|||
explicit env_universal_t(const wcstring &path);
|
||||
|
||||
// Get the value of the variable with the specified name.
|
||||
env_var_t get(const wcstring &name) const;
|
||||
maybe_t<env_var_t> get(const wcstring &name) const;
|
||||
|
||||
// Returns whether the variable with the given name is exported, or false if it does not exist.
|
||||
bool get_export(const wcstring &name) const;
|
||||
|
|
|
@ -376,10 +376,10 @@ void internal_exec(job_t *j, const io_chain_t &&all_ios) {
|
|||
// really make sense, so I'm not trying to fix it here.
|
||||
if (!setup_child_process(0, all_ios)) {
|
||||
// Decrement SHLVL as we're removing ourselves from the shell "stack".
|
||||
const env_var_t shlvl_var = env_get(L"SHLVL", ENV_GLOBAL | ENV_EXPORT);
|
||||
auto shlvl_var = env_get(L"SHLVL", ENV_GLOBAL | ENV_EXPORT);
|
||||
wcstring shlvl_str = L"0";
|
||||
if (!shlvl_var.missing()) {
|
||||
long shlvl = fish_wcstol(shlvl_var.as_string().c_str());
|
||||
if (shlvl_var) {
|
||||
long shlvl = fish_wcstol(shlvl_var->as_string().c_str());
|
||||
if (!errno && shlvl > 0) {
|
||||
shlvl_str = to_string<long>(shlvl - 1);
|
||||
}
|
||||
|
@ -1202,7 +1202,7 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, boo
|
|||
const int prev_status = proc_get_last_status();
|
||||
bool split_output = false;
|
||||
|
||||
const env_var_t ifs = env_get(L"IFS");
|
||||
const auto ifs = env_get(L"IFS");
|
||||
if (!ifs.missing_or_empty()) {
|
||||
split_output = true;
|
||||
}
|
||||
|
|
|
@ -769,19 +769,19 @@ static int expand_variables(const wcstring &instr, std::vector<completion_t> *ou
|
|||
}
|
||||
|
||||
var_tmp.append(instr, start_pos, var_len);
|
||||
env_var_t var;
|
||||
maybe_t<env_var_t> var;
|
||||
if (var_len == 1 && var_tmp[0] == VARIABLE_EXPAND_EMPTY) {
|
||||
var = missing_var;
|
||||
var = none();
|
||||
} else {
|
||||
var = env_get(var_tmp);
|
||||
}
|
||||
|
||||
if (!var.missing()) {
|
||||
if (var) {
|
||||
int all_vars = 1;
|
||||
wcstring_list_t var_item_list;
|
||||
|
||||
if (is_ok) {
|
||||
var.to_list(var_item_list);
|
||||
var->to_list(var_item_list);
|
||||
|
||||
const size_t slice_start = stop_pos;
|
||||
if (slice_start < insize && instr.at(slice_start) == L'[') {
|
||||
|
@ -1161,8 +1161,7 @@ static void expand_home_directory(wcstring &input) {
|
|||
size_t tail_idx;
|
||||
wcstring username = get_home_directory_name(input, &tail_idx);
|
||||
|
||||
bool tilde_error = false;
|
||||
env_var_t home;
|
||||
maybe_t<env_var_t> home;
|
||||
if (username.empty()) {
|
||||
// Current users home directory.
|
||||
home = env_get(L"HOME");
|
||||
|
@ -1178,16 +1177,13 @@ static void expand_home_directory(wcstring &input) {
|
|||
struct passwd *result;
|
||||
char buf[8192];
|
||||
int retval = getpwnam_r(name_cstr.c_str(), &userinfo, buf, sizeof(buf), &result);
|
||||
if (retval || !result) {
|
||||
tilde_error = true;
|
||||
} else {
|
||||
if (!retval && result) {
|
||||
home = env_var_t(L"HOME", str2wcstring(userinfo.pw_dir));
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t *realhome = wrealpath(home.as_string(), NULL);
|
||||
|
||||
if (!tilde_error && realhome) {
|
||||
const wchar_t *realhome = home ? wrealpath(home->as_string(), NULL) : nullptr;
|
||||
if (realhome) {
|
||||
input.replace(input.begin(), input.begin() + tail_idx, realhome);
|
||||
} else {
|
||||
input[0] = L'~';
|
||||
|
@ -1417,12 +1413,13 @@ static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector<
|
|||
} else {
|
||||
// Get the PATH/CDPATH and CWD. Perhaps these should be passed in. An empty CDPATH
|
||||
// implies just the current directory, while an empty PATH is left empty.
|
||||
env_var_t paths = env_get(for_cd ? L"CDPATH" : L"PATH");
|
||||
const wchar_t *name = for_cd ? L"CDPATH" : L"PATH";
|
||||
auto paths = env_get(name);
|
||||
if (paths.missing_or_empty()) {
|
||||
paths = env_var_t(paths.get_name(), for_cd ? L"." : L"");
|
||||
paths = env_var_t(name, for_cd ? L"." : L"");
|
||||
}
|
||||
|
||||
for (auto next_path : paths.as_list()) {
|
||||
for (auto next_path : paths->as_list()) {
|
||||
effective_working_dirs.push_back(
|
||||
path_apply_working_directory(next_path, working_dir));
|
||||
}
|
||||
|
@ -1575,9 +1572,9 @@ void update_abbr_cache(const wchar_t *op, const wcstring &varname) {
|
|||
}
|
||||
abbreviations.erase(abbr);
|
||||
if (wcscmp(op, L"ERASE") != 0) {
|
||||
const env_var_t expansion = env_get(varname);
|
||||
const auto expansion = env_get(varname);
|
||||
if (!expansion.missing_or_empty()) {
|
||||
abbreviations.emplace(std::make_pair(abbr, expansion.as_string()));
|
||||
abbreviations.emplace(std::make_pair(abbr, expansion->as_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2579,19 +2579,19 @@ static void test_universal() {
|
|||
for (int i = 0; i < threads; i++) {
|
||||
for (int j = 0; j < UVARS_PER_THREAD; j++) {
|
||||
const wcstring key = format_string(L"key_%d_%d", i, j);
|
||||
env_var_t expected_val;
|
||||
maybe_t<env_var_t> expected_val;
|
||||
if (j == 0) {
|
||||
expected_val = missing_var;
|
||||
expected_val = none();
|
||||
} else {
|
||||
expected_val = env_var_t(L"", format_string(L"val_%d_%d", i, j));
|
||||
}
|
||||
const env_var_t var = uvars.get(key);
|
||||
if (j == 0) assert(expected_val.missing());
|
||||
const maybe_t<env_var_t> var = uvars.get(key);
|
||||
if (j == 0) assert(!expected_val);
|
||||
if (var != expected_val) {
|
||||
const wchar_t *missing_desc = L"<missing>";
|
||||
err(L"Wrong value for key %ls: expected %ls, got %ls\n", key.c_str(),
|
||||
(expected_val.missing() ? missing_desc : expected_val.as_string().c_str()),
|
||||
(var.missing() ? missing_desc : var.as_string().c_str()));
|
||||
(expected_val ? expected_val->as_string().c_str() : missing_desc),
|
||||
(var ? var->as_string().c_str() : missing_desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4179,7 +4179,8 @@ long return_timezone_hour(time_t tstamp, const wchar_t *timezone) {
|
|||
|
||||
env_set_one(L"TZ", ENV_EXPORT, timezone);
|
||||
|
||||
const env_var_t var = env_get(L"TZ", ENV_DEFAULT);
|
||||
const auto var = env_get(L"TZ", ENV_DEFAULT);
|
||||
(void)var;
|
||||
|
||||
localtime_r(&tstamp, <ime);
|
||||
n = strftime(ltime_str, 3, "%H", <ime);
|
||||
|
|
|
@ -79,11 +79,11 @@ static int load(const wcstring &name) {
|
|||
static void autoload_names(std::unordered_set<wcstring> &names, int get_hidden) {
|
||||
size_t i;
|
||||
|
||||
const env_var_t path_var = env_get(L"fish_function_path");
|
||||
const auto path_var = env_get(L"fish_function_path");
|
||||
if (path_var.missing_or_empty()) return;
|
||||
|
||||
wcstring_list_t path_list;
|
||||
path_var.to_list(path_list);
|
||||
path_var->to_list(path_list);
|
||||
|
||||
for (i = 0; i < path_list.size(); i++) {
|
||||
const wcstring &ndir_str = path_list.at(i);
|
||||
|
@ -110,7 +110,8 @@ static void autoload_names(std::unordered_set<wcstring> &names, int get_hidden)
|
|||
static std::map<wcstring, env_var_t> snapshot_vars(const wcstring_list_t &vars) {
|
||||
std::map<wcstring, env_var_t> result;
|
||||
for (wcstring_list_t::const_iterator it = vars.begin(), end = vars.end(); it != end; ++it) {
|
||||
result.insert(std::make_pair(*it, env_get(*it)));
|
||||
auto var = env_get(*it);
|
||||
if (var) result.insert(std::make_pair(*it, std::move(*var)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -338,14 +339,6 @@ void function_prepare_environment(const wcstring &name, const wchar_t *const *ar
|
|||
}
|
||||
|
||||
for (auto it = inherited_vars.begin(), end = inherited_vars.end(); it != end; ++it) {
|
||||
// Note: Prior to my rewrite to address issue #4200 this code did the equivalent of this:
|
||||
// if (it->second.missing()) {
|
||||
// env_set_empty(it->first, ENV_LOCAL | ENV_USER);
|
||||
// } else {
|
||||
// It should be impossible for the var to be missing since we're inheriting it from an outer
|
||||
// scope. So we now die horribly if it is missing.
|
||||
assert(!it->second.missing());
|
||||
wcstring_list_t vals = it->second.as_list(); // we need a copy
|
||||
env_set(it->first, ENV_LOCAL | ENV_USER, vals); // because this mutates the list
|
||||
env_set(it->first, ENV_LOCAL | ENV_USER, it->second.as_list());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -221,12 +221,12 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d
|
|||
directories.push_back(working_directory);
|
||||
} else {
|
||||
// Get the CDPATH.
|
||||
env_var_t cdpath = env_get(L"CDPATH");
|
||||
if (cdpath.missing_or_empty()) cdpath = env_var_t(cdpath.get_name(), L".");
|
||||
auto cdpath = env_get(L"CDPATH");
|
||||
if (cdpath.missing_or_empty()) cdpath = env_var_t(L"CDPATH", L".");
|
||||
|
||||
// Tokenize it into directories.
|
||||
std::vector<wcstring> pathsv;
|
||||
cdpath.to_list(pathsv);
|
||||
cdpath->to_list(pathsv);
|
||||
for (auto next_path : pathsv) {
|
||||
if (next_path.empty()) next_path = L".";
|
||||
// Ensure that we use the working directory for relative cdpaths like ".".
|
||||
|
@ -268,27 +268,28 @@ rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background)
|
|||
return rgb_color_t::normal();
|
||||
}
|
||||
|
||||
env_var_t var = env_get(highlight_var[idx]);
|
||||
auto var = env_get(highlight_var[idx]);
|
||||
|
||||
// debug( 1, L"%d -> %d -> %ls", highlight, idx, val );
|
||||
|
||||
if (var.missing()) var = env_get(highlight_var[0]);
|
||||
if (!var) var = env_get(highlight_var[0]);
|
||||
|
||||
if (!var.missing()) result = parse_color(var, treat_as_background);
|
||||
if (var) result = parse_color(*var, treat_as_background);
|
||||
|
||||
// Handle modifiers.
|
||||
if (highlight & highlight_modifier_valid_path) {
|
||||
env_var_t var2 = env_get(L"fish_color_valid_path");
|
||||
|
||||
rgb_color_t result2 = parse_color(var2, is_background);
|
||||
if (result.is_normal())
|
||||
result = result2;
|
||||
else {
|
||||
if (result2.is_bold()) result.set_bold(true);
|
||||
if (result2.is_underline()) result.set_underline(true);
|
||||
if (result2.is_italics()) result.set_italics(true);
|
||||
if (result2.is_dim()) result.set_dim(true);
|
||||
if (result2.is_reverse()) result.set_reverse(true);
|
||||
auto var2 = env_get(L"fish_color_valid_path");
|
||||
if (var2) {
|
||||
rgb_color_t result2 = parse_color(*var2, is_background);
|
||||
if (result.is_normal())
|
||||
result = result2;
|
||||
else {
|
||||
if (result2.is_bold()) result.set_bold(true);
|
||||
if (result2.is_underline()) result.set_underline(true);
|
||||
if (result2.is_italics()) result.set_italics(true);
|
||||
if (result2.is_dim()) result.set_dim(true);
|
||||
if (result2.is_reverse()) result.set_reverse(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1780,9 +1780,9 @@ void history_sanity_check() {
|
|||
wcstring history_session_id() {
|
||||
wcstring result = DFLT_FISH_HISTORY_SESSION_ID;
|
||||
|
||||
const env_var_t var = env_get(L"FISH_HISTORY");
|
||||
if (!var.missing()) {
|
||||
wcstring session_id = var.as_string();
|
||||
const auto var = env_get(L"FISH_HISTORY");
|
||||
if (var) {
|
||||
wcstring session_id = var->as_string();
|
||||
if (session_id.empty()) {
|
||||
result = L"";
|
||||
} else if (session_id == L"default") {
|
||||
|
|
|
@ -201,8 +201,8 @@ static int input_function_args_index = 0;
|
|||
|
||||
/// Return the current bind mode.
|
||||
wcstring input_get_bind_mode() {
|
||||
env_var_t mode = env_get(FISH_BIND_MODE_VAR);
|
||||
return mode.missing() ? DEFAULT_BIND_MODE : mode.as_string();
|
||||
auto mode = env_get(FISH_BIND_MODE_VAR);
|
||||
return mode ? mode->as_string() : DEFAULT_BIND_MODE;
|
||||
}
|
||||
|
||||
/// Set the current bind mode.
|
||||
|
|
|
@ -158,18 +158,18 @@ static wint_t readb() {
|
|||
// 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() {
|
||||
env_var_t escape_time_ms = env_get(L"fish_escape_delay_ms");
|
||||
auto escape_time_ms = env_get(L"fish_escape_delay_ms");
|
||||
if (escape_time_ms.missing_or_empty()) {
|
||||
wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
long tmp = fish_wcstol(escape_time_ms.as_string().c_str());
|
||||
long tmp = fish_wcstol(escape_time_ms->as_string().c_str());
|
||||
if (errno || tmp < 10 || tmp >= 5000) {
|
||||
fwprintf(stderr,
|
||||
L"ignoring fish_escape_delay_ms: value '%ls' "
|
||||
L"is not an integer or is < 10 or >= 5000 ms\n",
|
||||
escape_time_ms.as_string().c_str());
|
||||
escape_time_ms->as_string().c_str());
|
||||
} else {
|
||||
wait_on_escape_ms = (int)tmp;
|
||||
}
|
||||
|
|
|
@ -554,10 +554,11 @@ void writembs_check(char *mbs, const char *mbs_name, const char *file, long line
|
|||
if (mbs != NULL) {
|
||||
tputs(mbs, 1, &writeb);
|
||||
} else {
|
||||
env_var_t term = env_get(L"TERM");
|
||||
auto term = env_get(L"TERM");
|
||||
const wchar_t *fmt =
|
||||
_(L"Tried to use terminfo string %s on line %ld of %s, which is "
|
||||
L"undefined in terminal of type \"%ls\". Please report this error to %s");
|
||||
debug(0, fmt, mbs_name, line, file, term.as_string().c_str(), PACKAGE_BUGREPORT);
|
||||
debug(0, fmt, mbs_name, line, file, term ? term->as_string().c_str() : L"",
|
||||
PACKAGE_BUGREPORT);
|
||||
}
|
||||
}
|
||||
|
|
30
src/path.cpp
30
src/path.cpp
|
@ -32,7 +32,7 @@
|
|||
const wcstring_list_t dflt_pathsv({L"/bin", L"/usr/bin", PREFIX L"/bin"});
|
||||
|
||||
static bool path_get_path_core(const wcstring &cmd, wcstring *out_path,
|
||||
const env_var_t &bin_path_var) {
|
||||
const maybe_t<env_var_t> &bin_path_var) {
|
||||
debug(3, L"path_get_path( '%ls' )", cmd.c_str());
|
||||
|
||||
// If the command has a slash, it must be an absolute or relative path and thus we don't bother
|
||||
|
@ -55,8 +55,8 @@ static bool path_get_path_core(const wcstring &cmd, wcstring *out_path,
|
|||
}
|
||||
|
||||
const wcstring_list_t *pathsv;
|
||||
if (!bin_path_var.missing()) {
|
||||
pathsv = &bin_path_var.as_list();
|
||||
if (bin_path_var) {
|
||||
pathsv = &bin_path_var->as_list();
|
||||
} else {
|
||||
pathsv = &dflt_pathsv;
|
||||
}
|
||||
|
@ -122,9 +122,9 @@ wcstring_list_t path_get_paths(const wcstring &cmd) {
|
|||
return paths;
|
||||
}
|
||||
|
||||
env_var_t path_var = env_get(L"PATH");
|
||||
auto path_var = env_get(L"PATH");
|
||||
std::vector<wcstring> pathsv;
|
||||
path_var.to_list(pathsv);
|
||||
if (path_var) path_var->to_list(pathsv);
|
||||
for (auto path : pathsv) {
|
||||
if (path.empty()) continue;
|
||||
append_path_component(path, cmd);
|
||||
|
@ -144,7 +144,7 @@ wcstring_list_t path_get_paths(const wcstring &cmd) {
|
|||
bool path_get_cdpath(const env_var_t &dir_var, wcstring *out, const wchar_t *wd,
|
||||
const env_vars_snapshot_t &env_vars) {
|
||||
int err = ENOENT;
|
||||
if (dir_var.missing_or_empty()) return false;
|
||||
if (dir_var.empty()) return false;
|
||||
wcstring dir = dir_var.as_string();
|
||||
|
||||
if (wd) {
|
||||
|
@ -165,11 +165,11 @@ bool path_get_cdpath(const env_var_t &dir_var, wcstring *out, const wchar_t *wd,
|
|||
paths.push_back(path);
|
||||
} else {
|
||||
// Respect CDPATH.
|
||||
env_var_t cdpaths = env_vars.get(L"CDPATH");
|
||||
if (cdpaths.missing_or_empty()) cdpaths = env_var_t(cdpaths.get_name(), L".");
|
||||
auto cdpaths = env_vars.get(L"CDPATH");
|
||||
if (cdpaths.missing_or_empty()) cdpaths = env_var_t(L"CDPATH", L".");
|
||||
|
||||
std::vector<wcstring> cdpathsv;
|
||||
cdpaths.to_list(cdpathsv);
|
||||
cdpaths->to_list(cdpathsv);
|
||||
for (auto next_path : cdpathsv) {
|
||||
if (next_path.empty()) next_path = L".";
|
||||
if (next_path == L"." && wd != NULL) {
|
||||
|
@ -260,8 +260,8 @@ static void maybe_issue_path_warning(const wcstring &which_dir, const wcstring &
|
|||
bool using_xdg, const wcstring &xdg_var, const wcstring &path,
|
||||
int saved_errno) {
|
||||
wcstring warning_var_name = L"_FISH_WARNED_" + which_dir;
|
||||
env_var_t var = env_get(warning_var_name, ENV_GLOBAL | ENV_EXPORT);
|
||||
if (var.missing()) return;
|
||||
auto var = env_get(warning_var_name, ENV_GLOBAL | ENV_EXPORT);
|
||||
if (!var) return;
|
||||
env_set_one(warning_var_name, ENV_GLOBAL | ENV_EXPORT, L"1");
|
||||
|
||||
debug(0, custom_error_msg.c_str());
|
||||
|
@ -288,19 +288,19 @@ static void path_create(wcstring &path, const wcstring &xdg_var, const wcstring
|
|||
// 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 env_var_t xdg_dir = env_get(xdg_var, ENV_GLOBAL | ENV_EXPORT);
|
||||
const auto xdg_dir = env_get(xdg_var, ENV_GLOBAL | ENV_EXPORT);
|
||||
if (!xdg_dir.missing_or_empty()) {
|
||||
using_xdg = true;
|
||||
path = xdg_dir.as_string() + L"/fish";
|
||||
path = xdg_dir->as_string() + L"/fish";
|
||||
if (create_directory(path) != -1) {
|
||||
path_done = true;
|
||||
} else {
|
||||
saved_errno = errno;
|
||||
}
|
||||
} else {
|
||||
const env_var_t home = env_get(L"HOME", ENV_GLOBAL | ENV_EXPORT);
|
||||
const auto home = env_get(L"HOME", ENV_GLOBAL | ENV_EXPORT);
|
||||
if (!home.missing_or_empty()) {
|
||||
path = home.as_string() +
|
||||
path = home->as_string() +
|
||||
(which_dir == L"config" ? L"/.config/fish" : L"/.local/share/fish");
|
||||
if (create_directory(path) != -1) {
|
||||
path_done = true;
|
||||
|
|
|
@ -2063,8 +2063,8 @@ void reader_import_history_if_necessary(void) {
|
|||
// Try opening a bash file. We make an effort to respect $HISTFILE; this isn't very complete
|
||||
// (AFAIK it doesn't have to be exported), and to really get this right we ought to ask bash
|
||||
// itself. But this is better than nothing.
|
||||
const env_var_t var = env_get(L"HISTFILE");
|
||||
wcstring path = (var.missing() ? L"~/.bash_history" : var.as_string());
|
||||
const auto var = env_get(L"HISTFILE");
|
||||
wcstring path = (var ? var->as_string() : L"~/.bash_history");
|
||||
expand_tilde(path);
|
||||
FILE *f = wfopen(path, "r");
|
||||
if (f) {
|
||||
|
|
Loading…
Reference in a new issue