From 6f52e6bb1c81b4ac9037cd4c603ef413c662d39c Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 21 Sep 2018 21:52:47 -0700 Subject: [PATCH] Instantize contents of exec.cpp and others --- src/autoload.cpp | 4 +- src/builtin.cpp | 2 +- src/builtin_argparse.cpp | 2 +- src/complete.cpp | 8 ++- src/exec.cpp | 18 +++---- src/exec.h | 5 +- src/expand.cpp | 39 ++++++++------ src/expand.h | 5 +- src/fish_tests.cpp | 111 ++++++++++++++++++++++----------------- src/highlight.cpp | 2 +- src/parser.cpp | 4 +- src/parser.h | 3 ++ src/path.cpp | 4 +- src/reader.cpp | 17 +++--- 14 files changed, 131 insertions(+), 93 deletions(-) diff --git a/src/autoload.cpp b/src/autoload.cpp index 8614731e3..18b75aef0 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -19,6 +19,7 @@ #include "common.h" #include "env.h" #include "exec.h" +#include "parser.h" #include "wutil.h" // IWYU pragma: keep /// The time before we'll recheck an autoloaded file. @@ -256,7 +257,8 @@ bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_ // If we have a script, either built-in or a file source, then run it. if (really_load && !script_source.empty()) { // Do nothing on failure. - exec_subshell(script_source, false /* do not apply exit status */); + exec_subshell(script_source, parser_t::principal_parser(), + false /* do not apply exit status */); } if (really_load) { diff --git a/src/builtin.cpp b/src/builtin.cpp index 2e3306f94..8e3ddeca5 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -169,7 +169,7 @@ wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t wcstring out; const wcstring name_esc = escape_string(name, 1); wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str()); - if (exec_subshell(cmd, lst, false /* don't apply exit status */) >= 0) { + if (exec_subshell(cmd, parser, lst, false /* don't apply exit status */) >= 0) { for (size_t i = 0; i < lst.size(); i++) { out.append(lst.at(i)); out.push_back(L'\n'); diff --git a/src/builtin_argparse.cpp b/src/builtin_argparse.cpp index df85cdd32..c4b88736e 100644 --- a/src/builtin_argparse.cpp +++ b/src/builtin_argparse.cpp @@ -453,7 +453,7 @@ static int validate_arg(parser_t &parser, const argparse_cmd_opts_t &opts, optio } vars.set_one(var_name_prefix + L"value", ENV_LOCAL, woptarg); - int retval = exec_subshell(opt_spec->validation_command, cmd_output, false); + int retval = exec_subshell(opt_spec->validation_command, parser, cmd_output, false); for (const auto &output : cmd_output) { streams.err.append(output); streams.err.push_back(L'\n'); diff --git a/src/complete.cpp b/src/complete.cpp index f9a633383..88c3208c0 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -410,7 +410,9 @@ bool completer_t::condition_test(const wcstring &condition) { condition_cache_t::iterator cached_entry = condition_cache.find(condition); if (cached_entry == condition_cache.end()) { // Compute new value and reinsert it. - test_res = (0 == exec_subshell(condition, false /* don't apply exit status */)); + // TODO: rationalize this parser_t usage. + test_res = (0 == exec_subshell(condition, parser_t::principal_parser(), + false /* don't apply exit status */)); condition_cache[condition] = test_res; } else { // Use the old value. @@ -591,7 +593,9 @@ void completer_t::complete_cmd_desc(const wcstring &str) { // search if we know the location of the whatis database. This can take some time on slower // systems with a large set of manuals, but it should be ok since apropos is only called once. wcstring_list_t list; - if (exec_subshell(lookup_cmd, list, false /* don't apply exit status */) != -1) { + // TODO: rationalize this use of principal_parser. + if (exec_subshell(lookup_cmd, parser_t::principal_parser(), list, + false /* don't apply exit status */) != -1) { std::unordered_map lookup; lookup.reserve(list.size()); diff --git a/src/exec.cpp b/src/exec.cpp index 25143e863..294f6eef7 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -369,7 +369,7 @@ void internal_exec(env_stack_t &vars, 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". - auto shlvl_var = env_get(L"SHLVL", ENV_GLOBAL | ENV_EXPORT); + auto shlvl_var = vars.get(L"SHLVL", ENV_GLOBAL | ENV_EXPORT); wcstring shlvl_str = L"0"; if (shlvl_var) { long shlvl = fish_wcstol(shlvl_var->as_string().c_str()); @@ -1085,14 +1085,14 @@ bool exec_job(parser_t &parser, shared_ptr j) { return true; } -static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, bool apply_exit_status, - bool is_subcmd) { +static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, wcstring_list_t *lst, + bool apply_exit_status, bool is_subcmd) { ASSERT_IS_MAIN_THREAD(); bool prev_subshell = is_subshell; const int prev_status = proc_get_last_status(); bool split_output = false; - const auto ifs = env_get(L"IFS"); + const auto ifs = parser.vars().get(L"IFS"); if (!ifs.missing_or_empty()) { split_output = true; } @@ -1163,13 +1163,13 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, boo return subcommand_status; } -int exec_subshell(const wcstring &cmd, std::vector &outputs, bool apply_exit_status, - bool is_subcmd) { +int exec_subshell(const wcstring &cmd, parser_t &parser, std::vector &outputs, + bool apply_exit_status, bool is_subcmd) { ASSERT_IS_MAIN_THREAD(); - return exec_subshell_internal(cmd, &outputs, apply_exit_status, is_subcmd); + return exec_subshell_internal(cmd, parser, &outputs, apply_exit_status, is_subcmd); } -int exec_subshell(const wcstring &cmd, bool apply_exit_status, bool is_subcmd) { +int exec_subshell(const wcstring &cmd, parser_t &parser, bool apply_exit_status, bool is_subcmd) { ASSERT_IS_MAIN_THREAD(); - return exec_subshell_internal(cmd, NULL, apply_exit_status, is_subcmd); + return exec_subshell_internal(cmd, parser, NULL, apply_exit_status, is_subcmd); } diff --git a/src/exec.h b/src/exec.h index 5e04fe3e5..010de4654 100644 --- a/src/exec.h +++ b/src/exec.h @@ -23,9 +23,10 @@ bool exec_job(parser_t &parser, std::shared_ptr j); /// \param outputs The list to insert output into. /// /// \return the status of the last job to exit, or -1 if en error was encountered. -int exec_subshell(const wcstring &cmd, std::vector &outputs, bool preserve_exit_status, +int exec_subshell(const wcstring &cmd, parser_t &parser, std::vector &outputs, + bool preserve_exit_status, bool is_subcmd = false); +int exec_subshell(const wcstring &cmd, parser_t &parser, bool preserve_exit_status, bool is_subcmd = false); -int exec_subshell(const wcstring &cmd, bool preserve_exit_status, bool is_subcmd = false); /// Loops over close until the syscall was run without being interrupted. void exec_close(int fd); diff --git a/src/expand.cpp b/src/expand.cpp index d885dada3..4beb86782 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -43,6 +43,7 @@ #include "iothread.h" #include "parse_constants.h" #include "parse_util.h" +#include "parser.h" #include "path.h" #include "proc.h" #include "reader.h" @@ -292,7 +293,7 @@ static size_t parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector *out, size_t last_idx, - parse_error_list_t *errors) { + const environment_t &vars, parse_error_list_t *errors) { const size_t insize = instr.size(); // last_idx may be 1 past the end of the string, but no further. @@ -356,7 +357,7 @@ static bool expand_variables(wcstring instr, std::vector *out, siz history = &history_t::history_with_name(history_session_id(env_stack_t::principal())); } } else if (var_name != wcstring{VARIABLE_EXPAND_EMPTY}) { - var = env_get(var_name); + var = vars.get(var_name); } // Parse out any following slice. @@ -406,7 +407,7 @@ static bool expand_variables(wcstring instr, std::vector *out, siz res.push_back(VARIABLE_EXPAND_EMPTY); } res.append(instr, var_name_and_slice_stop, wcstring::npos); - return expand_variables(std::move(res), out, varexp_char_idx, errors); + return expand_variables(std::move(res), out, varexp_char_idx, vars, errors); } } @@ -463,7 +464,7 @@ static bool expand_variables(wcstring instr, std::vector *out, siz // Append all entries in var_item_list, separated by the delimiter. res.append(join_strings(var_item_list, delimit)); res.append(instr, var_name_and_slice_stop, wcstring::npos); - return expand_variables(std::move(res), out, varexp_char_idx, errors); + return expand_variables(std::move(res), out, varexp_char_idx, vars, errors); } else { // Normal cartesian-product expansion. for (const wcstring &item : var_item_list) { @@ -480,7 +481,7 @@ static bool expand_variables(wcstring instr, std::vector *out, siz } new_in.append(item); new_in.append(instr, var_name_and_slice_stop, wcstring::npos); - if (!expand_variables(std::move(new_in), out, varexp_char_idx, errors)) { + if (!expand_variables(std::move(new_in), out, varexp_char_idx, vars, errors)) { return false; } } @@ -637,7 +638,10 @@ static bool expand_cmdsubst(const wcstring &input, std::vector *ou wcstring_list_t sub_res; const wcstring subcmd(paren_begin + 1, paren_end - paren_begin - 1); - if (exec_subshell(subcmd, sub_res, true /* apply_exit_status */, true /* is_subcmd */) == -1) { + // TODO: justify this parser_t::principal_parser + auto &parser = parser_t::principal_parser(); + if (exec_subshell(subcmd, parser, sub_res, true /* apply_exit_status */, + true /* is_subcmd */) == -1) { append_cmdsub_error(errors, SOURCE_LOCATION_UNKNOWN, L"Unknown error while evaulating command substitution"); return false; @@ -742,7 +746,7 @@ static wcstring get_home_directory_name(const wcstring &input, size_t *out_tail_ } /// Attempts tilde expansion of the string specified, modifying it in place. -static void expand_home_directory(wcstring &input) { +static void expand_home_directory(wcstring &input, const environment_t &vars) { if (!input.empty() && input.at(0) == HOME_DIRECTORY) { size_t tail_idx; wcstring username = get_home_directory_name(input, &tail_idx); @@ -750,7 +754,7 @@ static void expand_home_directory(wcstring &input) { maybe_t home; if (username.empty()) { // Current users home directory. - auto home_var = env_get(L"HOME"); + auto home_var = vars.get(L"HOME"); if (home_var.missing_or_empty()) { input.clear(); return; @@ -787,16 +791,17 @@ static void expand_percent_self(wcstring &input) { } } -void expand_tilde(wcstring &input) { +void expand_tilde(wcstring &input, const environment_t &vars) { // Avoid needless COW behavior by ensuring we use const at. const wcstring &tmp = input; if (!tmp.empty() && tmp.at(0) == L'~') { input.at(0) = HOME_DIRECTORY; - expand_home_directory(input); + expand_home_directory(input, vars); } } -static void unexpand_tildes(const wcstring &input, std::vector *completions) { +static void unexpand_tildes(const wcstring &input, const environment_t &vars, + std::vector *completions) { // If input begins with tilde, then try to replace the corresponding string in each completion // with the tilde. If it does not, there's nothing to do. if (input.empty() || input.at(0) != L'~') return; @@ -818,7 +823,7 @@ static void unexpand_tildes(const wcstring &input, std::vector *co // Expand username_with_tilde. wcstring home = username_with_tilde; - expand_tilde(home); + expand_tilde(home, vars); // Now for each completion that starts with home, replace it with the username_with_tilde. for (size_t i = 0; i < completions->size(); i++) { @@ -835,12 +840,12 @@ static void unexpand_tildes(const wcstring &input, std::vector *co // If the given path contains the user's home directory, replace that with a tilde. We don't try to // be smart about case insensitivity, etc. -wcstring replace_home_directory_with_tilde(const wcstring &str) { +wcstring replace_home_directory_with_tilde(const wcstring &str, const environment_t &vars) { // Only absolute paths get this treatment. wcstring result = str; if (string_prefixes_string(L"/", result)) { wcstring home_directory = L"~"; - expand_tilde(home_directory); + expand_tilde(home_directory, vars); if (!string_suffixes_string(L"/", home_directory)) { home_directory.push_back(L'/'); } @@ -928,7 +933,7 @@ static expand_error_t expand_stage_variables(wcstring input, std::vector *out_comp if (total_result != EXPAND_ERROR) { // Hack to un-expand tildes (see #647). if (!(flags & EXPAND_SKIP_HOME_DIRECTORIES)) { - unexpand_tildes(input, &completions); + unexpand_tildes(input, vars, &completions); } out_completions->insert(out_completions->end(), std::make_move_iterator(completions.begin()), diff --git a/src/expand.h b/src/expand.h index d64f64eed..4cf67e71c 100644 --- a/src/expand.h +++ b/src/expand.h @@ -17,6 +17,7 @@ #include "maybe.h" #include "parse_constants.h" +class environment_t; class env_var_t; class environment_t; @@ -152,10 +153,10 @@ wcstring expand_escape_variable(const env_var_t &var); /// Perform tilde expansion and nothing else on the specified string, which is modified in place. /// /// \param input the string to tilde expand -void expand_tilde(wcstring &input); +void expand_tilde(wcstring &input, const environment_t &vars); /// Perform the opposite of tilde expansion on the string, which is modified in place. -wcstring replace_home_directory_with_tilde(const wcstring &str); +wcstring replace_home_directory_with_tilde(const wcstring &str, const environment_t &vars); /// Abbreviation support. Expand src as an abbreviation, returning the expanded form if found, /// none() if not. diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index ce640296b..9efe2c2e7 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1502,11 +1502,17 @@ static void test_lru() { /// A crappy environment_t that only knows about PWD. struct pwd_environment_t : public environment_t { + std::map extras; + virtual maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override { if (key == L"PWD") { return env_var_t{wgetcwd(), 0}; } + auto extra = extras.find(key); + if (extra != extras.end()) { + return env_var_t(extra->second, ENV_DEFAULT); + } return {}; } @@ -2620,9 +2626,9 @@ static void test_completion_insertions() { } static void perform_one_autosuggestion_cd_test(const wcstring &command, const wcstring &expected, - long line) { + const environment_t &vars, long line) { std::vector comps; - complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, pwd_environment_t{}); + complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, vars); bool expects_error = (expected == L""); @@ -2694,7 +2700,6 @@ static void perform_one_completion_cd_test(const wcstring &command, const wcstri // Testing test_autosuggest_suggest_special, in particular for properly handling quotes and // backslashes. static void test_autosuggest_suggest_special() { - auto &vars = parser_t::principal_parser().vars(); if (system("mkdir -p 'test/autosuggest_test/0foobar'")) err(L"mkdir failed"); if (system("mkdir -p 'test/autosuggest_test/1foo bar'")) err(L"mkdir failed"); if (system("mkdir -p 'test/autosuggest_test/2foo bar'")) err(L"mkdir failed"); @@ -2724,60 +2729,72 @@ static void test_autosuggest_suggest_special() { const wcstring wd = L"test/autosuggest_test"; - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/5", L"foo\"bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/5", L"foo\"bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/5", L"foo\"bar/", __LINE__); + pwd_environment_t vars{}; + vars.extras[L"HOME"] = parser_t::principal_parser().vars().get(L"HOME")->as_string(); - vars.set_one(L"AUTOSUGGEST_TEST_LOC", ENV_LOCAL, wd); - perform_one_autosuggestion_cd_test(L"cd $AUTOSUGGEST_TEST_LOC/0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd ~/test_autosuggest_suggest_specia", L"l/", __LINE__); - - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/start/", L"unique2/unique3/", + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/1", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/1", L"foo bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/1", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/2", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/2", L"foo bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/2", L"foo bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/3", L"foo\\bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/3", L"foo\\bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/3", L"foo\\bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/4", L"foo'bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/4", L"foo'bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/4", L"foo'bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/5", L"foo\"bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/5", L"foo\"bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/5", L"foo\"bar/", vars, __LINE__); + vars.extras[L"AUTOSUGGEST_TEST_LOC"] = wd; + perform_one_autosuggestion_cd_test(L"cd $AUTOSUGGEST_TEST_LOC/0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd ~/test_autosuggest_suggest_specia", L"l/", vars, + __LINE__); + + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/start/", L"unique2/unique3/", + vars, __LINE__); + if (!pushd(wcs2string(wd).c_str())) return; - perform_one_autosuggestion_cd_test(L"cd 0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 5", L"foo\"bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"5", L"foo\"bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '5", L"foo\"bar/", __LINE__); + perform_one_autosuggestion_cd_test(L"cd 0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 1", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"1", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '1", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 2", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"2", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '2", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 3", L"foo\\bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"3", L"foo\\bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '3", L"foo\\bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 4", L"foo'bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"4", L"foo'bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '4", L"foo'bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 5", L"foo\"bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"5", L"foo\"bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '5", L"foo\"bar/", vars, __LINE__); // A single quote should defeat tilde expansion. - perform_one_autosuggestion_cd_test(L"cd '~/test_autosuggest_suggest_specia'", L"", + perform_one_autosuggestion_cd_test(L"cd '~/test_autosuggest_suggest_specia'", L"", vars, __LINE__); // Don't crash on ~ (issue #2696). Note this is cwd dependent. if (system("mkdir -p '~hahaha/path1/path2/'")) err(L"mkdir failed"); - perform_one_autosuggestion_cd_test(L"cd ~haha", L"ha/path1/path2/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd ~hahaha/", L"path1/path2/", __LINE__); + perform_one_autosuggestion_cd_test(L"cd ~haha", L"ha/path1/path2/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd ~hahaha/", L"path1/path2/", vars, __LINE__); perform_one_completion_cd_test(L"cd ~haha", L"ha/", __LINE__); perform_one_completion_cd_test(L"cd ~hahaha/", L"path1/", __LINE__); diff --git a/src/highlight.cpp b/src/highlight.cpp index 94a5ae52b..9b844f89f 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -114,7 +114,7 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l bool result = false; wcstring path_with_magic(potential_path_fragment); - if (flags & PATH_EXPAND_TILDE) expand_tilde(path_with_magic); + if (flags & PATH_EXPAND_TILDE) expand_tilde(path_with_magic, vars); // debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped ); diff --git a/src/parser.cpp b/src/parser.cpp index d70ad32e3..124fdbb6b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -96,8 +96,8 @@ static const struct block_lookup_entry block_lookup[] = { {(block_type_t)0, 0, 0}}; // Given a file path, return something nicer. Currently we just "unexpand" tildes. -static wcstring user_presentable_path(const wcstring &path) { - return replace_home_directory_with_tilde(path); +wcstring parser_t::user_presentable_path(const wcstring &path) const { + return replace_home_directory_with_tilde(path, vars()); } parser_t::parser_t() : variables(env_stack_t::principal()) {} diff --git a/src/parser.h b/src/parser.h index 33f28f8f0..6edea461e 100644 --- a/src/parser.h +++ b/src/parser.h @@ -199,6 +199,9 @@ class parser_t { /// every block if it is of type FUNCTION_CALL. const wchar_t *is_function(size_t idx = 0) const; + // Given a file path, return something nicer. Currently we just "unexpand" tildes. + wcstring user_presentable_path(const wcstring &path) const; + /// Helper for stack_trace(). void stack_trace_internal(size_t block_idx, wcstring *out) const; diff --git a/src/path.cpp b/src/path.cpp index 6a7b3673f..32c4932c9 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -187,7 +187,7 @@ maybe_t path_get_cdpath(const wcstring &dir, const wcstring &wd, // TODO: if next_path starts with ./ we need to replace the . with the wd. next_path = wd; } - expand_tilde(next_path); + expand_tilde(next_path, env_vars); if (next_path.empty()) continue; wcstring whole_path = next_path; @@ -213,7 +213,7 @@ maybe_t path_get_cdpath(const wcstring &dir, const wcstring &wd, maybe_t path_as_implicit_cd(const wcstring &path, const wcstring &wd, const environment_t &vars) { wcstring exp_path = path; - expand_tilde(exp_path); + expand_tilde(exp_path, vars); if (string_prefixes_string(L"/", exp_path) || string_prefixes_string(L"./", exp_path) || string_prefixes_string(L"../", exp_path) || string_suffixes_string(L"/", exp_path) || exp_path == L"..") { diff --git a/src/reader.cpp b/src/reader.cpp index e7737696a..339e9d360 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -400,6 +400,9 @@ class reader_data_t { /// Return the variable set used for e.g. command duration. env_stack_t &vars() { return parser_t::principal_parser().vars(); } + /// Hackish access to the parser. TODO: rationalize this. + parser_t &parser() { return parser_t::principal_parser(); } + const env_stack_t &vars() const { return parser_t::principal_parser().vars(); } /// Constructor @@ -829,7 +832,8 @@ void reader_write_title(const wcstring &cmd, bool reset_cursor_position) { wcstring_list_t lst; proc_push_interactive(0); - if (exec_subshell(fish_title_command, lst, false /* ignore exit status */) != -1 && + if (exec_subshell(fish_title_command, current_data()->parser(), lst, + false /* ignore exit status */) != -1 && !lst.empty()) { fputws(L"\x1B]0;", stdout); for (size_t i = 0; i < lst.size(); i++) { @@ -867,7 +871,8 @@ static void exec_prompt() { // Prepend any mode indicator to the left prompt (issue #1988). if (function_exists(MODE_PROMPT_FUNCTION_NAME)) { wcstring_list_t mode_indicator_list; - exec_subshell(MODE_PROMPT_FUNCTION_NAME, mode_indicator_list, apply_exit_status); + exec_subshell(MODE_PROMPT_FUNCTION_NAME, data->parser(), mode_indicator_list, + apply_exit_status); // We do not support multiple lines in the mode indicator, so just concatenate all of // them. for (size_t i = 0; i < mode_indicator_list.size(); i++) { @@ -878,7 +883,7 @@ static void exec_prompt() { if (!data->left_prompt.empty()) { wcstring_list_t prompt_list; // Ignore return status. - exec_subshell(data->left_prompt, prompt_list, apply_exit_status); + exec_subshell(data->left_prompt, data->parser(), prompt_list, apply_exit_status); for (size_t i = 0; i < prompt_list.size(); i++) { if (i > 0) data->left_prompt_buff += L'\n'; data->left_prompt_buff += prompt_list.at(i); @@ -888,7 +893,7 @@ static void exec_prompt() { if (!data->right_prompt.empty()) { wcstring_list_t prompt_list; // Status is ignored. - exec_subshell(data->right_prompt, prompt_list, apply_exit_status); + exec_subshell(data->right_prompt, data->parser(), prompt_list, apply_exit_status); for (size_t i = 0; i < prompt_list.size(); i++) { // Right prompt does not support multiple lines, so just concatenate all of them. data->right_prompt_buff += prompt_list.at(i); @@ -2155,9 +2160,9 @@ void reader_import_history_if_necessary() { // 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 auto var = env_get(L"HISTFILE"); + const auto var = data->vars().get(L"HISTFILE"); wcstring path = (var ? var->as_string() : L"~/.bash_history"); - expand_tilde(path); + expand_tilde(path, data->vars()); FILE *f = wfopen(path, "r"); if (f) { data->history->populate_from_bash(f);