Instantize env_get_pwd_slash

This requires threading environment_t through many places, such as completions
and history. We introduce null_environment_t for when the environment isn't
important.
This commit is contained in:
ridiculousfish 2018-09-10 22:29:52 -07:00
parent 26fc705c07
commit 421fbdd52a
15 changed files with 127 additions and 76 deletions

View File

@ -47,7 +47,7 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
dir_in = maybe_dir_in->as_string(); dir_in = maybe_dir_in->as_string();
} }
if (!path_get_cdpath(dir_in, &dir, env_get_pwd_slash())) { if (!path_get_cdpath(dir_in, &dir, parser.vars().get_pwd_slash())) {
if (errno == ENOTDIR) { if (errno == ENOTDIR) {
streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd, dir_in.c_str()); streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd, dir_in.c_str());
} else if (errno == ENOENT) { } else if (errno == ENOENT) {

View File

@ -535,7 +535,7 @@ void completer_t::complete_strings(const wcstring &wc_escaped, const description
const std::vector<completion_t> &possible_comp, const std::vector<completion_t> &possible_comp,
complete_flags_t flags) { complete_flags_t flags) {
wcstring tmp = wc_escaped; wcstring tmp = wc_escaped;
if (!expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags(), NULL)) if (!expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags(), vars))
return; return;
const wcstring wc = parse_util_unescape_wildcards(tmp); const wcstring wc = parse_util_unescape_wildcards(tmp);
@ -656,7 +656,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
expand_error_t result = expand_string(str_cmd, &this->completions, expand_error_t result = expand_string(str_cmd, &this->completions,
EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS | EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS |
EXECUTABLES_ONLY | this->expand_flags(), EXECUTABLES_ONLY | this->expand_flags(),
NULL); vars, NULL);
if (result != EXPAND_ERROR && this->wants_descriptions()) { if (result != EXPAND_ERROR && this->wants_descriptions()) {
this->complete_cmd_desc(str_cmd); this->complete_cmd_desc(str_cmd);
} }
@ -668,7 +668,8 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
expand_error_t ignore = expand_error_t ignore =
// Append all matching directories // Append all matching directories
expand_string(str_cmd, &this->completions, expand_string(str_cmd, &this->completions,
EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), NULL); EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), vars,
NULL);
UNUSED(ignore); UNUSED(ignore);
} }
@ -740,7 +741,7 @@ void completer_t::complete_from_args(const wcstring &str, const wcstring &args,
} }
std::vector<completion_t> possible_comp; std::vector<completion_t> possible_comp;
parser_t::expand_argument_list(args, eflags, &possible_comp); parser_t::expand_argument_list(args, eflags, vars, &possible_comp);
if (!is_autosuggest) { if (!is_autosuggest) {
proc_pop_interactive(); proc_pop_interactive();
@ -1079,7 +1080,7 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file,
// See #4954. // See #4954.
const wcstring sep_string = wcstring(str, sep_index + 1); const wcstring sep_string = wcstring(str, sep_index + 1);
std::vector<completion_t> local_completions; std::vector<completion_t> local_completions;
if (expand_string(sep_string, &local_completions, flags, NULL) == EXPAND_ERROR) { if (expand_string(sep_string, &local_completions, flags, vars, NULL) == EXPAND_ERROR) {
debug(3, L"Error while expanding string '%ls'", sep_string.c_str()); debug(3, L"Error while expanding string '%ls'", sep_string.c_str());
} }
@ -1098,7 +1099,7 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file,
// consider relaxing this if there was a preceding double-dash argument. // consider relaxing this if there was a preceding double-dash argument.
if (string_prefixes_string(L"-", str)) flags &= ~EXPAND_FUZZY_MATCH; if (string_prefixes_string(L"-", str)) flags &= ~EXPAND_FUZZY_MATCH;
if (expand_string(str, &this->completions, flags, NULL) == EXPAND_ERROR) { if (expand_string(str, &this->completions, flags, vars, NULL) == EXPAND_ERROR) {
debug(3, L"Error while expanding string '%ls'", str.c_str()); debug(3, L"Error while expanding string '%ls'", str.c_str());
} }
} }

View File

@ -677,7 +677,7 @@ void env_stack_t::set_read_limit() {
void env_stack_t::mark_changed_exported() { vars_stack().mark_changed_exported(); } void env_stack_t::mark_changed_exported() { vars_stack().mark_changed_exported(); }
wcstring env_stack_t::get_pwd_slash() { wcstring environment_t::get_pwd_slash() const {
// Return "/" if PWD is missing. // Return "/" if PWD is missing.
// See https://github.com/fish-shell/fish-shell/issues/5080 // See https://github.com/fish-shell/fish-shell/issues/5080
auto pwd_var = env_get(L"PWD"); auto pwd_var = env_get(L"PWD");
@ -1433,8 +1433,6 @@ int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) {
void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } void env_universal_barrier() { env_stack_t::principal().universal_barrier(); }
wcstring env_get_pwd_slash() { return env_stack_t::principal().get_pwd_slash(); }
void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); } void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); }
/// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported /// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported
@ -1606,6 +1604,13 @@ void env_stack_t::set_argv(const wchar_t *const *argv) {
environment_t::~environment_t() = default; environment_t::~environment_t() = default;
env_stack_t::~env_stack_t() = default; env_stack_t::~env_stack_t() = default;
null_environment_t::null_environment_t() = default;
null_environment_t::~null_environment_t() = default;
maybe_t<env_var_t> null_environment_t::get(const wcstring &key, env_mode_flags_t mode) const {
return none();
}
wcstring_list_t null_environment_t::get_names(int flags) const { return {}; }
env_stack_t &env_stack_t::principal() { env_stack_t &env_stack_t::principal() {
static env_stack_t s_principal; static env_stack_t s_principal;
return s_principal; return s_principal;

View File

@ -141,6 +141,19 @@ class environment_t {
env_mode_flags_t mode = ENV_DEFAULT) const = 0; env_mode_flags_t mode = ENV_DEFAULT) const = 0;
virtual wcstring_list_t get_names(int flags) const = 0; virtual wcstring_list_t get_names(int flags) const = 0;
virtual ~environment_t(); virtual ~environment_t();
/// Returns the PWD with a terminating slash.
wcstring get_pwd_slash() const;
};
/// The null environment contains nothing.
class null_environment_t : public environment_t {
public:
null_environment_t();
~null_environment_t() override;
maybe_t<env_var_t> get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override;
wcstring_list_t get_names(int flags) const override;
}; };
/// Gets the variable with the specified name, or none() if it does not exist. /// Gets the variable with the specified name, or none() if it does not exist.
@ -155,9 +168,6 @@ int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val);
/// Synchronizes all universal variable changes: writes everything out, reads stuff in. /// Synchronizes all universal variable changes: writes everything out, reads stuff in.
void env_universal_barrier(); void env_universal_barrier();
/// Returns the PWD with a terminating slash.
wcstring env_get_pwd_slash();
/// Update the read_byte_limit variable. /// Update the read_byte_limit variable.
void env_set_read_limit(); void env_set_read_limit();
@ -229,9 +239,6 @@ class env_stack_t : public environment_t {
/// Sets up argv as the given null terminated array of strings. /// Sets up argv as the given null terminated array of strings.
void set_argv(const wchar_t *const *argv); void set_argv(const wchar_t *const *argv);
/// Returns the PWD with a terminating slash.
wcstring get_pwd_slash();
/// Update the read_byte_limit variable. /// Update the read_byte_limit variable.
void set_read_limit(); void set_read_limit();

View File

@ -883,15 +883,17 @@ static void remove_internal_separator(wcstring *str, bool conv) {
} }
/// A stage in string expansion is represented as a function that takes an input and returns a list /// A stage in string expansion is represented as a function that takes an input and returns a list
/// of output (by reference). We get flags and errors. It may return an error; if so expansion /// of output (by reference). We get flags, vars and errors. It may return an error; if so expansion
/// halts. /// halts.
typedef expand_error_t (*expand_stage_t)(wcstring input, //!OCLINT(unused param) typedef expand_error_t (*expand_stage_t)(wcstring input, //!OCLINT(unused param)
std::vector<completion_t> *out, //!OCLINT(unused param) std::vector<completion_t> *out, //!OCLINT(unused param)
expand_flags_t flags, //!OCLINT(unused param) expand_flags_t flags, //!OCLINT(unused param)
const environment_t &vars, //!OCLINT(unused param)
parse_error_list_t *errors); //!OCLINT(unused param) parse_error_list_t *errors); //!OCLINT(unused param)
static expand_error_t expand_stage_cmdsubst(wcstring input, std::vector<completion_t> *out, static expand_error_t expand_stage_cmdsubst(wcstring input, std::vector<completion_t> *out,
expand_flags_t flags, parse_error_list_t *errors) { expand_flags_t flags, const environment_t &vars,
parse_error_list_t *errors) {
if (EXPAND_SKIP_CMDSUBST & flags) { if (EXPAND_SKIP_CMDSUBST & flags) {
wchar_t *begin, *end; wchar_t *begin, *end;
if (parse_util_locate_cmdsubst(input.c_str(), &begin, &end, true) == 0) { if (parse_util_locate_cmdsubst(input.c_str(), &begin, &end, true) == 0) {
@ -910,7 +912,8 @@ static expand_error_t expand_stage_cmdsubst(wcstring input, std::vector<completi
} }
static expand_error_t expand_stage_variables(wcstring input, std::vector<completion_t> *out, static expand_error_t expand_stage_variables(wcstring input, std::vector<completion_t> *out,
expand_flags_t flags, parse_error_list_t *errors) { expand_flags_t flags, const environment_t &vars,
parse_error_list_t *errors) {
// We accept incomplete strings here, since complete uses expand_string to expand incomplete // We accept incomplete strings here, since complete uses expand_string to expand incomplete
// strings from the commandline. // strings from the commandline.
wcstring next; wcstring next;
@ -933,12 +936,14 @@ static expand_error_t expand_stage_variables(wcstring input, std::vector<complet
} }
static expand_error_t expand_stage_braces(wcstring input, std::vector<completion_t> *out, static expand_error_t expand_stage_braces(wcstring input, std::vector<completion_t> *out,
expand_flags_t flags, parse_error_list_t *errors) { expand_flags_t flags, const environment_t &vars,
parse_error_list_t *errors) {
return expand_braces(input, flags, out, errors); return expand_braces(input, flags, out, errors);
} }
static expand_error_t expand_stage_home_and_self(wcstring input, std::vector<completion_t> *out, static expand_error_t expand_stage_home_and_self(wcstring input, std::vector<completion_t> *out,
expand_flags_t flags, parse_error_list_t *errors) { expand_flags_t flags, const environment_t &vars,
parse_error_list_t *errors) {
(void)errors; (void)errors;
if (!(EXPAND_SKIP_HOME_DIRECTORIES & flags)) { if (!(EXPAND_SKIP_HOME_DIRECTORIES & flags)) {
expand_home_directory(input); expand_home_directory(input);
@ -948,8 +953,8 @@ static expand_error_t expand_stage_home_and_self(wcstring input, std::vector<com
return EXPAND_OK; return EXPAND_OK;
} }
static expand_error_t expand_stage_wildcards(wcstring path_to_expand, static expand_error_t expand_stage_wildcards(wcstring path_to_expand, std::vector<completion_t> *out,
std::vector<completion_t> *out, expand_flags_t flags, expand_flags_t flags, const environment_t &vars,
parse_error_list_t *errors) { parse_error_list_t *errors) {
UNUSED(errors); UNUSED(errors);
expand_error_t result = EXPAND_OK; expand_error_t result = EXPAND_OK;
@ -968,7 +973,7 @@ static expand_error_t expand_stage_wildcards(wcstring path_to_expand,
// //
// So we're going to treat this input as a file path. Compute the "working directories", // So we're going to treat this input as a file path. Compute the "working directories",
// which may be CDPATH if the special flag is set. // which may be CDPATH if the special flag is set.
const wcstring working_dir = env_get_pwd_slash(); const wcstring working_dir = vars.get_pwd_slash();
wcstring_list_t effective_working_dirs; wcstring_list_t effective_working_dirs;
bool for_cd = static_cast<bool>(flags & EXPAND_SPECIAL_FOR_CD); bool for_cd = static_cast<bool>(flags & EXPAND_SPECIAL_FOR_CD);
bool for_command = static_cast<bool>(flags & EXPAND_SPECIAL_FOR_COMMAND); bool for_command = static_cast<bool>(flags & EXPAND_SPECIAL_FOR_COMMAND);
@ -1042,7 +1047,8 @@ static expand_error_t expand_stage_wildcards(wcstring path_to_expand,
} }
expand_error_t expand_string(wcstring input, std::vector<completion_t> *out_completions, expand_error_t expand_string(wcstring input, std::vector<completion_t> *out_completions,
expand_flags_t flags, parse_error_list_t *errors) { expand_flags_t flags, const environment_t &vars,
parse_error_list_t *errors) {
// Early out. If we're not completing, and there's no magic in the input, we're done. // Early out. If we're not completing, and there's no magic in the input, we're done.
if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(input)) { if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(input)) {
append_completion(out_completions, std::move(input)); append_completion(out_completions, std::move(input));
@ -1064,7 +1070,7 @@ expand_error_t expand_string(wcstring input, std::vector<completion_t> *out_comp
for (size_t i = 0; total_result != EXPAND_ERROR && i < completions.size(); i++) { for (size_t i = 0; total_result != EXPAND_ERROR && i < completions.size(); i++) {
wcstring &next = completions.at(i).completion; wcstring &next = completions.at(i).completion;
expand_error_t this_result = expand_error_t this_result =
stages[stage_idx](std::move(next), &output_storage, flags, errors); stages[stage_idx](std::move(next), &output_storage, flags, vars, errors);
// If this_result was no match, but total_result is that we have a match, then don't // If this_result was no match, but total_result is that we have a match, then don't
// change it. // change it.
if (!(this_result == EXPAND_WILDCARD_NO_MATCH && if (!(this_result == EXPAND_WILDCARD_NO_MATCH &&
@ -1090,14 +1096,15 @@ expand_error_t expand_string(wcstring input, std::vector<completion_t> *out_comp
return total_result; return total_result;
} }
bool expand_one(wcstring &string, expand_flags_t flags, parse_error_list_t *errors) { bool expand_one(wcstring &string, expand_flags_t flags, const environment_t &vars,
parse_error_list_t *errors) {
std::vector<completion_t> completions; std::vector<completion_t> completions;
if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(string)) { if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(string)) {
return true; return true;
} }
if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, errors) && if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, vars, errors) &&
completions.size() == 1) { completions.size() == 1) {
string = std::move(completions.at(0).completion); string = std::move(completions.at(0).completion);
return true; return true;
@ -1105,8 +1112,9 @@ bool expand_one(wcstring &string, expand_flags_t flags, parse_error_list_t *erro
return false; return false;
} }
expand_error_t expand_to_command_and_args(const wcstring &instr, wcstring *out_cmd, expand_error_t expand_to_command_and_args(const wcstring &instr, const environment_t &vars,
wcstring_list_t *out_args, parse_error_list_t *errors) { wcstring *out_cmd, wcstring_list_t *out_args,
parse_error_list_t *errors) {
// Fast path. // Fast path.
if (expand_is_clean(instr)) { if (expand_is_clean(instr)) {
*out_cmd = instr; *out_cmd = instr;
@ -1114,9 +1122,9 @@ expand_error_t expand_to_command_and_args(const wcstring &instr, wcstring *out_c
} }
std::vector<completion_t> completions; std::vector<completion_t> completions;
expand_error_t expand_err = expand_error_t expand_err = expand_string(
expand_string(instr, &completions, instr, &completions, EXPAND_SKIP_CMDSUBST | EXPAND_NO_DESCRIPTIONS | EXPAND_SKIP_JOBS, vars,
EXPAND_SKIP_CMDSUBST | EXPAND_NO_DESCRIPTIONS | EXPAND_SKIP_JOBS, errors); errors);
if (expand_err == EXPAND_OK || expand_err == EXPAND_WILDCARD_MATCH) { if (expand_err == EXPAND_OK || expand_err == EXPAND_WILDCARD_MATCH) {
// The first completion is the command, any remaning are arguments. // The first completion is the command, any remaning are arguments.
bool first = true; bool first = true;

View File

@ -17,6 +17,7 @@
#include "parse_constants.h" #include "parse_constants.h"
class env_var_t; class env_var_t;
class environment_t;
enum { enum {
/// Flag specifying that cmdsubst expansion should be skipped. /// Flag specifying that cmdsubst expansion should be skipped.
@ -111,13 +112,15 @@ enum expand_error_t {
/// \param output The list to which the result will be appended. /// \param output The list to which the result will be appended.
/// \param flags Specifies if any expansion pass should be skipped. Legal values are any combination /// \param flags Specifies if any expansion pass should be skipped. Legal values are any combination
/// of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS /// of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS
/// \param vars variables used during expansion.
/// \param errors Resulting errors, or NULL to ignore /// \param errors Resulting errors, or NULL to ignore
/// ///
/// \return One of EXPAND_OK, EXPAND_ERROR, EXPAND_WILDCARD_MATCH and EXPAND_WILDCARD_NO_MATCH. /// \return One of EXPAND_OK, EXPAND_ERROR, EXPAND_WILDCARD_MATCH and EXPAND_WILDCARD_NO_MATCH.
/// EXPAND_WILDCARD_NO_MATCH and EXPAND_WILDCARD_MATCH are normal exit conditions used only on /// EXPAND_WILDCARD_NO_MATCH and EXPAND_WILDCARD_MATCH are normal exit conditions used only on
/// strings containing wildcards to tell if the wildcard produced any matches. /// strings containing wildcards to tell if the wildcard produced any matches.
__warn_unused expand_error_t expand_string(wcstring input, std::vector<completion_t> *output, __warn_unused expand_error_t expand_string(wcstring input, std::vector<completion_t> *output,
expand_flags_t flags, parse_error_list_t *errors); expand_flags_t flags, const environment_t &vars,
parse_error_list_t *errors);
/// expand_one is identical to expand_string, except it will fail if in expands to more than one /// expand_one is identical to expand_string, except it will fail if in expands to more than one
/// string. This is used for expanding command names. /// string. This is used for expanding command names.
@ -128,7 +131,8 @@ __warn_unused expand_error_t expand_string(wcstring input, std::vector<completio
/// \param errors Resulting errors, or NULL to ignore /// \param errors Resulting errors, or NULL to ignore
/// ///
/// \return Whether expansion succeded /// \return Whether expansion succeded
bool expand_one(wcstring &inout_str, expand_flags_t flags, parse_error_list_t *errors = NULL); bool expand_one(wcstring &inout_str, expand_flags_t flags, const environment_t &vars,
parse_error_list_t *errors = NULL);
/// Expand a command string like $HOME/bin/cmd into a command and list of arguments. /// Expand a command string like $HOME/bin/cmd into a command and list of arguments.
/// Return the command and arguments by reference. /// Return the command and arguments by reference.
@ -136,8 +140,8 @@ bool expand_one(wcstring &inout_str, expand_flags_t flags, parse_error_list_t *e
/// that API does not distinguish between expansion resulting in an empty command (''), and /// that API does not distinguish between expansion resulting in an empty command (''), and
/// expansion resulting in no command (e.g. unset variable). /// expansion resulting in no command (e.g. unset variable).
// \return an expand error. // \return an expand error.
expand_error_t expand_to_command_and_args(const wcstring &instr, wcstring *out_cmd, expand_error_t expand_to_command_and_args(const wcstring &instr, const environment_t &vars,
wcstring_list_t *out_args, wcstring *out_cmd, wcstring_list_t *out_args,
parse_error_list_t *errors = NULL); parse_error_list_t *errors = NULL);
/// Convert the variable value to a human readable form, i.e. escape things, handle arrays, etc. /// Convert the variable value to a human readable form, i.e. escape things, handle arrays, etc.

View File

@ -911,7 +911,7 @@ static void test_parser() {
say(L"Testing eval_args"); say(L"Testing eval_args");
completion_list_t comps; completion_list_t comps;
parser_t::expand_argument_list(L"alpha 'beta gamma' delta", 0, &comps); parser_t::expand_argument_list(L"alpha 'beta gamma' delta", 0, null_environment_t{}, &comps);
do_test(comps.size() == 3); do_test(comps.size() == 3);
do_test(comps.at(0).completion == L"alpha"); do_test(comps.at(0).completion == L"alpha");
do_test(comps.at(1).completion == L"beta gamma"); do_test(comps.at(1).completion == L"beta gamma");
@ -1500,6 +1500,19 @@ static void test_lru() {
do_test(cache.evicted.size() == size_t(total_nodes)); do_test(cache.evicted.size() == size_t(total_nodes));
} }
/// A crappy environment_t that only knows about PWD.
struct pwd_environment_t : public environment_t {
virtual maybe_t<env_var_t> get(const wcstring &key,
env_mode_flags_t mode = ENV_DEFAULT) const override {
if (key == L"PWD") {
return env_var_t{wgetcwd(), 0};
}
return {};
}
wcstring_list_t get_names(int flags) const override { return {L"PWD"}; }
};
/// Perform parameter expansion and test if the output equals the zero-terminated parameter list /// Perform parameter expansion and test if the output equals the zero-terminated parameter list
/// supplied. /// supplied.
/// ///
@ -1515,7 +1528,7 @@ static bool expand_test(const wchar_t *in, expand_flags_t flags, ...) {
wchar_t *arg; wchar_t *arg;
parse_error_list_t errors; parse_error_list_t errors;
if (expand_string(in, &output, flags, &errors) == EXPAND_ERROR) { if (expand_string(in, &output, flags, pwd_environment_t{}, &errors) == EXPAND_ERROR) {
if (errors.empty()) { if (errors.empty()) {
err(L"Bug: Parse error reported but no error text found."); err(L"Bug: Parse error reported but no error text found.");
} else { } else {
@ -2173,7 +2186,7 @@ static bool run_test_test(int expected, const wcstring &str) {
// We need to tokenize the string in the same manner a normal shell would do. This is because we // We need to tokenize the string in the same manner a normal shell would do. This is because we
// need to test things like quoted strings that have leading and trailing whitespace. // need to test things like quoted strings that have leading and trailing whitespace.
parser_t::expand_argument_list(str, 0, &comps); parser_t::expand_argument_list(str, 0, null_environment_t{}, &comps);
for (completion_list_t::const_iterator it = comps.begin(), end = comps.end(); it != end; ++it) { for (completion_list_t::const_iterator it = comps.begin(), end = comps.end(); it != end; ++it) {
argv.push_back(it->completion); argv.push_back(it->completion);
} }
@ -2341,6 +2354,9 @@ static void test_complete() {
maybe_t<env_var_t> get(const wcstring &key, maybe_t<env_var_t> get(const wcstring &key,
env_mode_flags_t mode = ENV_DEFAULT) const override { env_mode_flags_t mode = ENV_DEFAULT) const override {
if (key == L"PWD") {
return env_var_t{wgetcwd(), 0};
}
return {}; return {};
} }
}; };
@ -2604,7 +2620,7 @@ static void test_completion_insertions() {
static void perform_one_autosuggestion_cd_test(const wcstring &command, const wcstring &expected, static void perform_one_autosuggestion_cd_test(const wcstring &command, const wcstring &expected,
long line) { long line) {
std::vector<completion_t> comps; std::vector<completion_t> comps;
complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, env_vars_snapshot_t{}); complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, pwd_environment_t{});
bool expects_error = (expected == L"<error>"); bool expects_error = (expected == L"<error>");

View File

@ -243,11 +243,11 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d
// appropriately for commands. If we succeed, return true. // appropriately for commands. If we succeed, return true.
static bool plain_statement_get_expanded_command(const wcstring &src, static bool plain_statement_get_expanded_command(const wcstring &src,
tnode_t<g::plain_statement> stmt, tnode_t<g::plain_statement> stmt,
wcstring *out_cmd) { const environment_t &vars, wcstring *out_cmd) {
// Get the command. Try expanding it. If we cannot, it's an error. // Get the command. Try expanding it. If we cannot, it's an error.
maybe_t<wcstring> cmd = command_for_plain_statement(stmt, src); maybe_t<wcstring> cmd = command_for_plain_statement(stmt, src);
if (!cmd) return false; if (!cmd) return false;
expand_error_t err = expand_to_command_and_args(*cmd, out_cmd, nullptr); expand_error_t err = expand_to_command_and_args(*cmd, vars, out_cmd, nullptr);
return err == EXPAND_OK || err == EXPAND_WILDCARD_MATCH; return err == EXPAND_OK || err == EXPAND_WILDCARD_MATCH;
} }
@ -310,8 +310,8 @@ static bool has_expand_reserved(const wcstring &str) {
// Parse a command line. Return by reference the last command, and the last argument to that command // Parse a command line. Return by reference the last command, and the last argument to that command
// (as a string), if any. This is used by autosuggestions. // (as a string), if any. This is used by autosuggestions.
static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expanded_command, static bool autosuggest_parse_command(const wcstring &buff, const environment_t &vars,
wcstring *out_last_arg) { wcstring *out_expanded_command, wcstring *out_last_arg) {
// Parse the buffer. // Parse the buffer.
parse_node_tree_t parse_tree; parse_node_tree_t parse_tree;
parse_tree_from_string(buff, parse_tree_from_string(buff,
@ -321,7 +321,7 @@ static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expand
// Find the last statement. // Find the last statement.
auto last_statement = parse_tree.find_last_node<g::plain_statement>(); auto last_statement = parse_tree.find_last_node<g::plain_statement>();
if (last_statement && if (last_statement &&
plain_statement_get_expanded_command(buff, last_statement, out_expanded_command)) { plain_statement_get_expanded_command(buff, last_statement, vars, out_expanded_command)) {
// Find the last argument. If we don't get one, return an invalid node. // Find the last argument. If we don't get one, return an invalid node.
if (auto last_arg = parse_tree.find_last_node<g::argument>(last_statement)) { if (auto last_arg = parse_tree.find_last_node<g::argument>(last_statement)) {
*out_last_arg = last_arg.get_source(buff); *out_last_arg = last_arg.get_source(buff);
@ -341,11 +341,11 @@ bool autosuggest_validate_from_history(const history_item_t &item,
// Parse the string. // Parse the string.
wcstring parsed_command; wcstring parsed_command;
wcstring cd_dir; wcstring cd_dir;
if (!autosuggest_parse_command(item.str(), &parsed_command, &cd_dir)) return false; if (!autosuggest_parse_command(item.str(), vars, &parsed_command, &cd_dir)) return false;
if (parsed_command == L"cd" && !cd_dir.empty()) { if (parsed_command == L"cd" && !cd_dir.empty()) {
// We can possibly handle this specially. // We can possibly handle this specially.
if (expand_one(cd_dir, EXPAND_SKIP_CMDSUBST)) { if (expand_one(cd_dir, EXPAND_SKIP_CMDSUBST, vars)) {
handled = true; handled = true;
bool is_help = bool is_help =
string_prefixes_string(cd_dir, L"--help") || string_prefixes_string(cd_dir, L"-h"); string_prefixes_string(cd_dir, L"--help") || string_prefixes_string(cd_dir, L"-h");
@ -823,7 +823,7 @@ bool highlighter_t::is_cd(tnode_t<g::plain_statement> stmt) const {
bool cmd_is_cd = false; bool cmd_is_cd = false;
if (this->io_ok && stmt.has_source()) { if (this->io_ok && stmt.has_source()) {
wcstring cmd_str; wcstring cmd_str;
if (plain_statement_get_expanded_command(this->buff, stmt, &cmd_str)) { if (plain_statement_get_expanded_command(this->buff, stmt, vars, &cmd_str)) {
cmd_is_cd = (cmd_str == L"cd"); cmd_is_cd = (cmd_str == L"cd");
} }
} }
@ -840,7 +840,7 @@ void highlighter_t::color_arguments(const std::vector<tnode_t<g::argument>> &arg
if (cmd_is_cd) { if (cmd_is_cd) {
// Mark this as an error if it's not 'help' and not a valid cd path. // Mark this as an error if it's not 'help' and not a valid cd path.
wcstring param = arg.get_source(this->buff); wcstring param = arg.get_source(this->buff);
if (expand_one(param, EXPAND_SKIP_CMDSUBST)) { if (expand_one(param, EXPAND_SKIP_CMDSUBST, vars)) {
bool is_help = string_prefixes_string(param, L"--help") || bool is_help = string_prefixes_string(param, L"--help") ||
string_prefixes_string(param, L"-h"); string_prefixes_string(param, L"-h");
if (!is_help && this->io_ok && if (!is_help && this->io_ok &&
@ -883,7 +883,7 @@ void highlighter_t::color_redirection(tnode_t<g::redirection> redirection_node)
// I/O is disallowed, so we don't have much hope of catching anything but gross // I/O is disallowed, so we don't have much hope of catching anything but gross
// errors. Assume it's valid. // errors. Assume it's valid.
target_is_valid = true; target_is_valid = true;
} else if (!expand_one(target, EXPAND_SKIP_CMDSUBST)) { } else if (!expand_one(target, EXPAND_SKIP_CMDSUBST, vars)) {
// Could not be expanded. // Could not be expanded.
target_is_valid = false; target_is_valid = false;
} else { } else {
@ -1117,7 +1117,8 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
wcstring expanded_cmd; wcstring expanded_cmd;
// Check to see if the command is valid. // Check to see if the command is valid.
// Try expanding it. If we cannot, it's an error. // Try expanding it. If we cannot, it's an error.
bool expanded = plain_statement_get_expanded_command(buff, stmt, &expanded_cmd); bool expanded =
plain_statement_get_expanded_command(buff, stmt, vars, &expanded_cmd);
if (expanded && !has_expand_reserved(expanded_cmd)) { if (expanded && !has_expand_reserved(expanded_cmd)) {
is_valid_cmd = is_valid_cmd =
command_is_valid(expanded_cmd, decoration, working_directory, vars); command_is_valid(expanded_cmd, decoration, working_directory, vars);
@ -1200,7 +1201,7 @@ void highlight_shell(const wcstring &buff, std::vector<highlight_spec_t> &color,
UNUSED(error); UNUSED(error);
// Do something sucky and get the current working directory on this background thread. This // Do something sucky and get the current working directory on this background thread. This
// should really be passed in. // should really be passed in.
const wcstring working_directory = env_get_pwd_slash(); const wcstring working_directory = vars.get_pwd_slash();
// Highlight it! // Highlight it!
highlighter_t highlighter(buff, pos, vars, working_directory, true /* can do IO */); highlighter_t highlighter(buff, pos, vars, working_directory, true /* can do IO */);
@ -1212,7 +1213,7 @@ void highlight_shell_no_io(const wcstring &buff, std::vector<highlight_spec_t> &
UNUSED(error); UNUSED(error);
// Do something sucky and get the current working directory on this background thread. This // Do something sucky and get the current working directory on this background thread. This
// should really be passed in. // should really be passed in.
const wcstring working_directory = env_get_pwd_slash(); const wcstring working_directory = vars.get_pwd_slash();
// Highlight it! // Highlight it!
highlighter_t highlighter(buff, pos, vars, working_directory, false /* no IO allowed */); highlighter_t highlighter(buff, pos, vars, working_directory, false /* no IO allowed */);

View File

@ -1918,7 +1918,8 @@ static bool string_could_be_path(const wcstring &potential_path) {
return true; return true;
} }
void history_t::add_pending_with_file_detection(const wcstring &str) { void history_t::add_pending_with_file_detection(const wcstring &str,
const wcstring &working_dir_slash) {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
// Find all arguments that look like they could be file paths. // Find all arguments that look like they could be file paths.
@ -1967,8 +1968,7 @@ void history_t::add_pending_with_file_detection(const wcstring &str) {
// Check for which paths are valid on a background thread, // Check for which paths are valid on a background thread,
// then on the main thread update our history item // then on the main thread update our history item
const wcstring wd = env_get_pwd_slash(); iothread_perform([=]() { return valid_paths(potential_paths, working_dir_slash); },
iothread_perform([=]() { return valid_paths(potential_paths, wd); },
[=](path_list_t validated_paths) { [=](path_list_t validated_paths) {
this->set_valid_file_paths(validated_paths, identifier); this->set_valid_file_paths(validated_paths, identifier);
this->enable_automatic_saving(); this->enable_automatic_saving();

View File

@ -229,7 +229,7 @@ class history_t {
// Add a new pending history item to the end, and then begin file detection on the items to // Add a new pending history item to the end, and then begin file detection on the items to
// determine which arguments are paths // determine which arguments are paths
void add_pending_with_file_detection(const wcstring &str); void add_pending_with_file_detection(const wcstring &str, const wcstring &working_dir_slash);
// Resolves any pending history items, so that they may be returned in history searches. // Resolves any pending history items, so that they may be returned in history searches.
void resolve_pending(); void resolve_pending();

View File

@ -121,13 +121,14 @@ tnode_t<g::plain_statement> parse_execution_context_t::infinite_recursive_statem
// are not infinite recursion. In particular that is what enables 'wrapper functions'. // are not infinite recursion. In particular that is what enables 'wrapper functions'.
tnode_t<g::statement> statement = first_job.child<0>(); tnode_t<g::statement> statement = first_job.child<0>();
tnode_t<g::job_continuation> continuation = first_job.child<1>(); tnode_t<g::job_continuation> continuation = first_job.child<1>();
const null_environment_t nullenv{};
while (statement) { while (statement) {
tnode_t<g::plain_statement> plain_statement = tnode_t<g::plain_statement> plain_statement =
statement.try_get_child<g::decorated_statement, 0>() statement.try_get_child<g::decorated_statement, 0>()
.try_get_child<g::plain_statement, 0>(); .try_get_child<g::plain_statement, 0>();
if (plain_statement) { if (plain_statement) {
maybe_t<wcstring> cmd = command_for_plain_statement(plain_statement, pstree->src); maybe_t<wcstring> cmd = command_for_plain_statement(plain_statement, pstree->src);
if (cmd && expand_one(*cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL) && if (cmd && expand_one(*cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, nullenv) &&
cmd == forbidden_function_name) { cmd == forbidden_function_name) {
// This is it. // This is it.
infinite_recursive_statement = plain_statement; infinite_recursive_statement = plain_statement;
@ -371,7 +372,7 @@ parse_execution_result_t parse_execution_context_t::run_for_statement(
// in just one. // in just one.
tnode_t<g::tok_string> var_name_node = header.child<1>(); tnode_t<g::tok_string> var_name_node = header.child<1>();
wcstring for_var_name = get_source(var_name_node); wcstring for_var_name = get_source(var_name_node);
if (!expand_one(for_var_name, 0, NULL)) { if (!expand_one(for_var_name, 0, parser->vars())) {
report_error(var_name_node, FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG, for_var_name.c_str()); report_error(var_name_node, FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG, for_var_name.c_str());
return parse_execution_errored; return parse_execution_errored;
} }
@ -438,8 +439,8 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement(
// Expand it. We need to offset any errors by the position of the string. // Expand it. We need to offset any errors by the position of the string.
std::vector<completion_t> switch_values_expanded; std::vector<completion_t> switch_values_expanded;
parse_error_list_t errors; parse_error_list_t errors;
int expand_ret = int expand_ret = expand_string(switch_value, &switch_values_expanded, EXPAND_NO_DESCRIPTIONS,
expand_string(switch_value, &switch_values_expanded, EXPAND_NO_DESCRIPTIONS, &errors); parser->vars(), &errors);
parse_error_offset_source_start(&errors, switch_value_n.source_range()->start); parse_error_offset_source_start(&errors, switch_value_n.source_range()->start);
switch (expand_ret) { switch (expand_ret) {
@ -722,7 +723,8 @@ parse_execution_result_t parse_execution_context_t::expand_command(
wcstring_list_t args; wcstring_list_t args;
// Expand the string to produce completions, and report errors. // Expand the string to produce completions, and report errors.
expand_error_t expand_err = expand_to_command_and_args(unexp_cmd, out_cmd, out_args, &errors); expand_error_t expand_err =
expand_to_command_and_args(unexp_cmd, parser->vars(), out_cmd, out_args, &errors);
if (expand_err == EXPAND_ERROR) { if (expand_err == EXPAND_ERROR) {
proc_set_last_status(STATUS_ILLEGAL_CMD); proc_set_last_status(STATUS_ILLEGAL_CMD);
return report_errors(errors); return report_errors(errors);
@ -813,7 +815,7 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process(
// Ok, no arguments or redirections; check to see if the command is a directory. // Ok, no arguments or redirections; check to see if the command is a directory.
wcstring implicit_cd_path; wcstring implicit_cd_path;
use_implicit_cd = use_implicit_cd =
path_can_be_implicit_cd(cmd, env_get_pwd_slash(), &implicit_cd_path); path_can_be_implicit_cd(cmd, parser->vars().get_pwd_slash(), &implicit_cd_path);
} }
} }
@ -883,7 +885,8 @@ parse_execution_result_t parse_execution_context_t::expand_arguments_from_nodes(
// Expand this string. // Expand this string.
parse_error_list_t errors; parse_error_list_t errors;
arg_expanded.clear(); arg_expanded.clear();
int expand_ret = expand_string(arg_str, &arg_expanded, EXPAND_NO_DESCRIPTIONS, &errors); int expand_ret =
expand_string(arg_str, &arg_expanded, EXPAND_NO_DESCRIPTIONS, parser->vars(), &errors);
parse_error_offset_source_start(&errors, arg_node.source_range()->start); parse_error_offset_source_start(&errors, arg_node.source_range()->start);
switch (expand_ret) { switch (expand_ret) {
case EXPAND_ERROR: { case EXPAND_ERROR: {
@ -931,7 +934,8 @@ bool parse_execution_context_t::determine_io_chain(tnode_t<g::arguments_or_redir
auto redirect_type = redirection_type(redirect_node, pstree->src, &source_fd, &target); auto redirect_type = redirection_type(redirect_node, pstree->src, &source_fd, &target);
// PCA: I can't justify this EXPAND_SKIP_VARIABLES flag. It was like this when I got here. // PCA: I can't justify this EXPAND_SKIP_VARIABLES flag. It was like this when I got here.
bool target_expanded = expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0, NULL); bool target_expanded =
expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0, parser->vars());
if (!target_expanded || target.empty()) { if (!target_expanded || target.empty()) {
// TODO: Improve this error message. // TODO: Improve this error message.
errored = errored =

View File

@ -19,6 +19,7 @@
#include "future_feature_flags.h" #include "future_feature_flags.h"
#include "parse_constants.h" #include "parse_constants.h"
#include "parse_util.h" #include "parse_util.h"
#include "parser.h"
#include "tnode.h" #include "tnode.h"
#include "tokenizer.h" #include "tokenizer.h"
#include "util.h" #include "util.h"
@ -1131,8 +1132,8 @@ static bool detect_errors_in_plain_statement(const wcstring &buff_src,
if (maybe_t<wcstring> unexp_command = command_for_plain_statement(pst, buff_src)) { if (maybe_t<wcstring> unexp_command = command_for_plain_statement(pst, buff_src)) {
wcstring command; wcstring command;
// Check that we can expand the command. // Check that we can expand the command.
if (expand_to_command_and_args(*unexp_command, &command, nullptr, parse_errors) == if (expand_to_command_and_args(*unexp_command, null_environment_t{}, &command, nullptr,
EXPAND_ERROR) { parse_errors) == EXPAND_ERROR) {
errored = true; errored = true;
} }

View File

@ -314,6 +314,7 @@ void parser_t::emit_profiling(const char *path) const {
} }
void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t eflags, void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t eflags,
const environment_t &vars,
std::vector<completion_t> *output_arg_list) { std::vector<completion_t> *output_arg_list) {
assert(output_arg_list != NULL); assert(output_arg_list != NULL);
@ -330,7 +331,8 @@ void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t
tnode_t<grammar::freestanding_argument_list> arg_list(&tree, &tree.at(0)); tnode_t<grammar::freestanding_argument_list> arg_list(&tree, &tree.at(0));
while (auto arg = arg_list.next_in_list<grammar::argument>()) { while (auto arg = arg_list.next_in_list<grammar::argument>()) {
const wcstring arg_src = arg.get_source(arg_list_src); const wcstring arg_src = arg.get_source(arg_list_src);
if (expand_string(arg_src, output_arg_list, eflags, NULL) == EXPAND_ERROR) { if (expand_string(arg_src, output_arg_list, eflags, vars, NULL /* errors */) ==
EXPAND_ERROR) {
break; // failed to expand a string break; // failed to expand a string
} }
} }

View File

@ -247,7 +247,7 @@ class parser_t {
/// \param flags Some expand flags to use /// \param flags Some expand flags to use
/// \param output List to insert output into /// \param output List to insert output into
static void expand_argument_list(const wcstring &arg_src, expand_flags_t flags, static void expand_argument_list(const wcstring &arg_src, expand_flags_t flags,
std::vector<completion_t> *output); const environment_t &vars, std::vector<completion_t> *output);
/// Returns a string describing the current parser position in the format 'FILENAME (line /// Returns a string describing the current parser position in the format 'FILENAME (line
/// LINE_NUMBER): LINE'. Example: /// LINE_NUMBER): LINE'. Example:

View File

@ -1277,10 +1277,10 @@ struct autosuggestion_result_t {
// on a background thread) to determine the autosuggestion // on a background thread) to determine the autosuggestion
static std::function<autosuggestion_result_t(void)> get_autosuggestion_performer( static std::function<autosuggestion_result_t(void)> get_autosuggestion_performer(
const wcstring &search_string, size_t cursor_pos, history_t *history) { const wcstring &search_string, size_t cursor_pos, history_t *history) {
const auto &parser_vars = parser_t::principal_parser().vars();
const unsigned int generation_count = read_generation_count(); const unsigned int generation_count = read_generation_count();
const wcstring working_directory(env_get_pwd_slash()); const wcstring working_directory = parser_vars.get_pwd_slash();
env_vars_snapshot_t vars(parser_t::principal_parser().vars(), env_vars_snapshot_t vars(vars, env_vars_snapshot_t::highlighting_keys);
env_vars_snapshot_t::highlighting_keys);
// TODO: suspicious use of 'history' here // TODO: suspicious use of 'history' here
// This is safe because histories are immortal, but perhaps // This is safe because histories are immortal, but perhaps
// this should use shared_ptr // this should use shared_ptr
@ -2473,6 +2473,8 @@ const wchar_t *reader_readline(int nchars) {
s_reset(&data->screen, screen_reset_abandon_line); s_reset(&data->screen, screen_reset_abandon_line);
reader_repaint(); reader_repaint();
const auto &vars = parser_t::principal_parser().vars();
// Get the current terminal modes. These will be restored when the function returns. // Get the current terminal modes. These will be restored when the function returns.
if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output(); if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output();
// Set the new modes. // Set the new modes.
@ -2685,8 +2687,7 @@ const wchar_t *reader_readline(int nchars) {
complete_flags_t complete_flags = COMPLETION_REQUEST_DEFAULT | complete_flags_t complete_flags = COMPLETION_REQUEST_DEFAULT |
COMPLETION_REQUEST_DESCRIPTIONS | COMPLETION_REQUEST_DESCRIPTIONS |
COMPLETION_REQUEST_FUZZY_MATCH; COMPLETION_REQUEST_FUZZY_MATCH;
data->complete_func(buffcpy, &comp, complete_flags, data->complete_func(buffcpy, &comp, complete_flags, vars);
parser_t::principal_parser().vars());
// Munge our completions. // Munge our completions.
completions_sort_and_prioritize(&comp); completions_sort_and_prioritize(&comp);
@ -2893,7 +2894,8 @@ const wchar_t *reader_readline(int nchars) {
// space. // space.
const editable_line_t *el = &data->command_line; const editable_line_t *el = &data->command_line;
if (data->history != NULL && !el->empty() && el->text.at(0) != L' ') { if (data->history != NULL && !el->empty() && el->text.at(0) != L' ') {
data->history->add_pending_with_file_detection(el->text); data->history->add_pending_with_file_detection(el->text,
vars.get_pwd_slash());
} }
finished = 1; finished = 1;
update_buff_pos(&data->command_line, data->command_line.size()); update_buff_pos(&data->command_line, data->command_line.size());