Instantize contents of exec.cpp and others

This commit is contained in:
ridiculousfish 2018-09-21 21:52:47 -07:00
parent 038f3cca6d
commit 6f52e6bb1c
14 changed files with 131 additions and 93 deletions

View File

@ -19,6 +19,7 @@
#include "common.h" #include "common.h"
#include "env.h" #include "env.h"
#include "exec.h" #include "exec.h"
#include "parser.h"
#include "wutil.h" // IWYU pragma: keep #include "wutil.h" // IWYU pragma: keep
/// The time before we'll recheck an autoloaded file. /// 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 we have a script, either built-in or a file source, then run it.
if (really_load && !script_source.empty()) { if (really_load && !script_source.empty()) {
// Do nothing on failure. // 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) { if (really_load) {

View File

@ -169,7 +169,7 @@ wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t
wcstring out; wcstring out;
const wcstring name_esc = escape_string(name, 1); const wcstring name_esc = escape_string(name, 1);
wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str()); 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++) { for (size_t i = 0; i < lst.size(); i++) {
out.append(lst.at(i)); out.append(lst.at(i));
out.push_back(L'\n'); out.push_back(L'\n');

View File

@ -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); 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) { for (const auto &output : cmd_output) {
streams.err.append(output); streams.err.append(output);
streams.err.push_back(L'\n'); streams.err.push_back(L'\n');

View File

@ -410,7 +410,9 @@ bool completer_t::condition_test(const wcstring &condition) {
condition_cache_t::iterator cached_entry = condition_cache.find(condition); condition_cache_t::iterator cached_entry = condition_cache.find(condition);
if (cached_entry == condition_cache.end()) { if (cached_entry == condition_cache.end()) {
// Compute new value and reinsert it. // 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; condition_cache[condition] = test_res;
} else { } else {
// Use the old value. // 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 // 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. // systems with a large set of manuals, but it should be ok since apropos is only called once.
wcstring_list_t list; 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<wcstring, wcstring> lookup; std::unordered_map<wcstring, wcstring> lookup;
lookup.reserve(list.size()); lookup.reserve(list.size());

View File

@ -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. // really make sense, so I'm not trying to fix it here.
if (!setup_child_process(0, all_ios)) { if (!setup_child_process(0, all_ios)) {
// Decrement SHLVL as we're removing ourselves from the shell "stack". // 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"; wcstring shlvl_str = L"0";
if (shlvl_var) { if (shlvl_var) {
long shlvl = fish_wcstol(shlvl_var->as_string().c_str()); long shlvl = fish_wcstol(shlvl_var->as_string().c_str());
@ -1085,14 +1085,14 @@ bool exec_job(parser_t &parser, shared_ptr<job_t> j) {
return true; return true;
} }
static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, bool apply_exit_status, static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, wcstring_list_t *lst,
bool is_subcmd) { bool apply_exit_status, bool is_subcmd) {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
bool prev_subshell = is_subshell; bool prev_subshell = is_subshell;
const int prev_status = proc_get_last_status(); const int prev_status = proc_get_last_status();
bool split_output = false; bool split_output = false;
const auto ifs = env_get(L"IFS"); const auto ifs = parser.vars().get(L"IFS");
if (!ifs.missing_or_empty()) { if (!ifs.missing_or_empty()) {
split_output = true; split_output = true;
} }
@ -1163,13 +1163,13 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, boo
return subcommand_status; return subcommand_status;
} }
int exec_subshell(const wcstring &cmd, std::vector<wcstring> &outputs, bool apply_exit_status, int exec_subshell(const wcstring &cmd, parser_t &parser, std::vector<wcstring> &outputs,
bool is_subcmd) { bool apply_exit_status, bool is_subcmd) {
ASSERT_IS_MAIN_THREAD(); 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(); 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);
} }

View File

@ -23,9 +23,10 @@ bool exec_job(parser_t &parser, std::shared_ptr<job_t> j);
/// \param outputs The list to insert output into. /// \param outputs The list to insert output into.
/// ///
/// \return the status of the last job to exit, or -1 if en error was encountered. /// \return the status of the last job to exit, or -1 if en error was encountered.
int exec_subshell(const wcstring &cmd, std::vector<wcstring> &outputs, bool preserve_exit_status, int exec_subshell(const wcstring &cmd, parser_t &parser, std::vector<wcstring> &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); 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. /// Loops over close until the syscall was run without being interrupted.
void exec_close(int fd); void exec_close(int fd);

View File

@ -43,6 +43,7 @@
#include "iothread.h" #include "iothread.h"
#include "parse_constants.h" #include "parse_constants.h"
#include "parse_util.h" #include "parse_util.h"
#include "parser.h"
#include "path.h" #include "path.h"
#include "proc.h" #include "proc.h"
#include "reader.h" #include "reader.h"
@ -292,7 +293,7 @@ static size_t parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector<long
/// actually starts operating on last_idx-1. As such, to process a string fully, pass string.size() /// actually starts operating on last_idx-1. As such, to process a string fully, pass string.size()
/// as last_idx instead of string.size()-1. /// as last_idx instead of string.size()-1.
static bool expand_variables(wcstring instr, std::vector<completion_t> *out, size_t last_idx, static bool expand_variables(wcstring instr, std::vector<completion_t> *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(); const size_t insize = instr.size();
// last_idx may be 1 past the end of the string, but no further. // 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<completion_t> *out, siz
history = &history_t::history_with_name(history_session_id(env_stack_t::principal())); history = &history_t::history_with_name(history_session_id(env_stack_t::principal()));
} }
} else if (var_name != wcstring{VARIABLE_EXPAND_EMPTY}) { } else if (var_name != wcstring{VARIABLE_EXPAND_EMPTY}) {
var = env_get(var_name); var = vars.get(var_name);
} }
// Parse out any following slice. // Parse out any following slice.
@ -406,7 +407,7 @@ static bool expand_variables(wcstring instr, std::vector<completion_t> *out, siz
res.push_back(VARIABLE_EXPAND_EMPTY); res.push_back(VARIABLE_EXPAND_EMPTY);
} }
res.append(instr, var_name_and_slice_stop, wcstring::npos); 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<completion_t> *out, siz
// Append all entries in var_item_list, separated by the delimiter. // Append all entries in var_item_list, separated by the delimiter.
res.append(join_strings(var_item_list, delimit)); res.append(join_strings(var_item_list, delimit));
res.append(instr, var_name_and_slice_stop, wcstring::npos); 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 { } else {
// Normal cartesian-product expansion. // Normal cartesian-product expansion.
for (const wcstring &item : var_item_list) { for (const wcstring &item : var_item_list) {
@ -480,7 +481,7 @@ static bool expand_variables(wcstring instr, std::vector<completion_t> *out, siz
} }
new_in.append(item); new_in.append(item);
new_in.append(instr, var_name_and_slice_stop, wcstring::npos); 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; return false;
} }
} }
@ -637,7 +638,10 @@ static bool expand_cmdsubst(const wcstring &input, std::vector<completion_t> *ou
wcstring_list_t sub_res; wcstring_list_t sub_res;
const wcstring subcmd(paren_begin + 1, paren_end - paren_begin - 1); 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, append_cmdsub_error(errors, SOURCE_LOCATION_UNKNOWN,
L"Unknown error while evaulating command substitution"); L"Unknown error while evaulating command substitution");
return false; 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. /// 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) { if (!input.empty() && input.at(0) == HOME_DIRECTORY) {
size_t tail_idx; size_t tail_idx;
wcstring username = get_home_directory_name(input, &tail_idx); wcstring username = get_home_directory_name(input, &tail_idx);
@ -750,7 +754,7 @@ static void expand_home_directory(wcstring &input) {
maybe_t<wcstring> home; maybe_t<wcstring> home;
if (username.empty()) { if (username.empty()) {
// Current users home directory. // Current users home directory.
auto home_var = env_get(L"HOME"); auto home_var = vars.get(L"HOME");
if (home_var.missing_or_empty()) { if (home_var.missing_or_empty()) {
input.clear(); input.clear();
return; 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. // Avoid needless COW behavior by ensuring we use const at.
const wcstring &tmp = input; const wcstring &tmp = input;
if (!tmp.empty() && tmp.at(0) == L'~') { if (!tmp.empty() && tmp.at(0) == L'~') {
input.at(0) = HOME_DIRECTORY; input.at(0) = HOME_DIRECTORY;
expand_home_directory(input); expand_home_directory(input, vars);
} }
} }
static void unexpand_tildes(const wcstring &input, std::vector<completion_t> *completions) { static void unexpand_tildes(const wcstring &input, const environment_t &vars,
std::vector<completion_t> *completions) {
// If input begins with tilde, then try to replace the corresponding string in each completion // 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. // with the tilde. If it does not, there's nothing to do.
if (input.empty() || input.at(0) != L'~') return; if (input.empty() || input.at(0) != L'~') return;
@ -818,7 +823,7 @@ static void unexpand_tildes(const wcstring &input, std::vector<completion_t> *co
// Expand username_with_tilde. // Expand username_with_tilde.
wcstring home = 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. // Now for each completion that starts with home, replace it with the username_with_tilde.
for (size_t i = 0; i < completions->size(); i++) { for (size_t i = 0; i < completions->size(); i++) {
@ -835,12 +840,12 @@ static void unexpand_tildes(const wcstring &input, std::vector<completion_t> *co
// If the given path contains the user's home directory, replace that with a tilde. We don't try to // 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. // 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. // Only absolute paths get this treatment.
wcstring result = str; wcstring result = str;
if (string_prefixes_string(L"/", result)) { if (string_prefixes_string(L"/", result)) {
wcstring home_directory = L"~"; wcstring home_directory = L"~";
expand_tilde(home_directory); expand_tilde(home_directory, vars);
if (!string_suffixes_string(L"/", home_directory)) { if (!string_suffixes_string(L"/", home_directory)) {
home_directory.push_back(L'/'); home_directory.push_back(L'/');
} }
@ -928,7 +933,7 @@ static expand_error_t expand_stage_variables(wcstring input, std::vector<complet
append_completion(out, std::move(next)); append_completion(out, std::move(next));
} else { } else {
size_t size = next.size(); size_t size = next.size();
if (!expand_variables(std::move(next), out, size, errors)) { if (!expand_variables(std::move(next), out, size, vars, errors)) {
return EXPAND_ERROR; return EXPAND_ERROR;
} }
} }
@ -946,7 +951,7 @@ static expand_error_t expand_stage_home_and_self(wcstring input, std::vector<com
parse_error_list_t *errors) { 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, vars);
} }
expand_percent_self(input); expand_percent_self(input);
append_completion(out, std::move(input)); append_completion(out, std::move(input));
@ -1087,7 +1092,7 @@ expand_error_t expand_string(wcstring input, std::vector<completion_t> *out_comp
if (total_result != EXPAND_ERROR) { if (total_result != EXPAND_ERROR) {
// Hack to un-expand tildes (see #647). // Hack to un-expand tildes (see #647).
if (!(flags & EXPAND_SKIP_HOME_DIRECTORIES)) { if (!(flags & EXPAND_SKIP_HOME_DIRECTORIES)) {
unexpand_tildes(input, &completions); unexpand_tildes(input, vars, &completions);
} }
out_completions->insert(out_completions->end(), out_completions->insert(out_completions->end(),
std::make_move_iterator(completions.begin()), std::make_move_iterator(completions.begin()),

View File

@ -17,6 +17,7 @@
#include "maybe.h" #include "maybe.h"
#include "parse_constants.h" #include "parse_constants.h"
class environment_t;
class env_var_t; class env_var_t;
class environment_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. /// Perform tilde expansion and nothing else on the specified string, which is modified in place.
/// ///
/// \param input the string to tilde expand /// \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. /// 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, /// Abbreviation support. Expand src as an abbreviation, returning the expanded form if found,
/// none() if not. /// none() if not.

View File

@ -1502,11 +1502,17 @@ static void test_lru() {
/// A crappy environment_t that only knows about PWD. /// A crappy environment_t that only knows about PWD.
struct pwd_environment_t : public environment_t { struct pwd_environment_t : public environment_t {
std::map<wcstring, wcstring> extras;
virtual maybe_t<env_var_t> get(const wcstring &key, virtual 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") { if (key == L"PWD") {
return env_var_t{wgetcwd(), 0}; return env_var_t{wgetcwd(), 0};
} }
auto extra = extras.find(key);
if (extra != extras.end()) {
return env_var_t(extra->second, ENV_DEFAULT);
}
return {}; return {};
} }
@ -2620,9 +2626,9 @@ 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) { const environment_t &vars, long line) {
std::vector<completion_t> comps; std::vector<completion_t> comps;
complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, pwd_environment_t{}); complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, vars);
bool expects_error = (expected == L"<error>"); bool expects_error = (expected == L"<error>");
@ -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 // Testing test_autosuggest_suggest_special, in particular for properly handling quotes and
// backslashes. // backslashes.
static void test_autosuggest_suggest_special() { 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/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/1foo bar'")) err(L"mkdir failed");
if (system("mkdir -p 'test/autosuggest_test/2foo 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"; const wcstring wd = L"test/autosuggest_test";
perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/0", L"foobar/", __LINE__); pwd_environment_t vars{};
perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/0", L"foobar/", __LINE__); vars.extras[L"HOME"] = parser_t::principal_parser().vars().get(L"HOME")->as_string();
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__);
vars.set_one(L"AUTOSUGGEST_TEST_LOC", ENV_LOCAL, wd); perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/0", L"foobar/", vars, __LINE__);
perform_one_autosuggestion_cd_test(L"cd $AUTOSUGGEST_TEST_LOC/0", L"foobar/", __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_suggest_specia", L"l/", __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/start/", L"unique2/unique3/", 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__); __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; 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/", vars, __LINE__);
perform_one_autosuggestion_cd_test(L"cd \"0", L"foobar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd \"0", L"foobar/", vars, __LINE__);
perform_one_autosuggestion_cd_test(L"cd '0", L"foobar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd '0", L"foobar/", vars, __LINE__);
perform_one_autosuggestion_cd_test(L"cd 1", L"foo bar/", __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/", __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/", __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/", __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/", __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/", __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/", __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/", __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/", __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/", __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/", __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/", __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/", __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/", __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/", __LINE__); perform_one_autosuggestion_cd_test(L"cd '5", L"foo\"bar/", vars, __LINE__);
// A single quote should defeat tilde expansion. // A single quote should defeat tilde expansion.
perform_one_autosuggestion_cd_test(L"cd '~/test_autosuggest_suggest_specia'", L"<error>", perform_one_autosuggestion_cd_test(L"cd '~/test_autosuggest_suggest_specia'", L"<error>", vars,
__LINE__); __LINE__);
// Don't crash on ~ (issue #2696). Note this is cwd dependent. // Don't crash on ~ (issue #2696). Note this is cwd dependent.
if (system("mkdir -p '~hahaha/path1/path2/'")) err(L"mkdir failed"); 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 ~haha", L"ha/path1/path2/", vars, __LINE__);
perform_one_autosuggestion_cd_test(L"cd ~hahaha/", L"path1/path2/", __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 ~haha", L"ha/", __LINE__);
perform_one_completion_cd_test(L"cd ~hahaha/", L"path1/", __LINE__); perform_one_completion_cd_test(L"cd ~hahaha/", L"path1/", __LINE__);

View File

@ -114,7 +114,7 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l
bool result = false; bool result = false;
wcstring path_with_magic(potential_path_fragment); 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 ); // debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped );

View File

@ -96,8 +96,8 @@ static const struct block_lookup_entry block_lookup[] = {
{(block_type_t)0, 0, 0}}; {(block_type_t)0, 0, 0}};
// Given a file path, return something nicer. Currently we just "unexpand" tildes. // Given a file path, return something nicer. Currently we just "unexpand" tildes.
static wcstring user_presentable_path(const wcstring &path) { wcstring parser_t::user_presentable_path(const wcstring &path) const {
return replace_home_directory_with_tilde(path); return replace_home_directory_with_tilde(path, vars());
} }
parser_t::parser_t() : variables(env_stack_t::principal()) {} parser_t::parser_t() : variables(env_stack_t::principal()) {}

View File

@ -199,6 +199,9 @@ class parser_t {
/// every block if it is of type FUNCTION_CALL. /// every block if it is of type FUNCTION_CALL.
const wchar_t *is_function(size_t idx = 0) const; 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(). /// Helper for stack_trace().
void stack_trace_internal(size_t block_idx, wcstring *out) const; void stack_trace_internal(size_t block_idx, wcstring *out) const;

View File

@ -187,7 +187,7 @@ maybe_t<wcstring> path_get_cdpath(const wcstring &dir, const wcstring &wd,
// TODO: if next_path starts with ./ we need to replace the . with the wd. // TODO: if next_path starts with ./ we need to replace the . with the wd.
next_path = wd; next_path = wd;
} }
expand_tilde(next_path); expand_tilde(next_path, env_vars);
if (next_path.empty()) continue; if (next_path.empty()) continue;
wcstring whole_path = next_path; wcstring whole_path = next_path;
@ -213,7 +213,7 @@ maybe_t<wcstring> path_get_cdpath(const wcstring &dir, const wcstring &wd,
maybe_t<wcstring> path_as_implicit_cd(const wcstring &path, const wcstring &wd, maybe_t<wcstring> path_as_implicit_cd(const wcstring &path, const wcstring &wd,
const environment_t &vars) { const environment_t &vars) {
wcstring exp_path = path; 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) || 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) || string_prefixes_string(L"../", exp_path) || string_suffixes_string(L"/", exp_path) ||
exp_path == L"..") { exp_path == L"..") {

View File

@ -400,6 +400,9 @@ class reader_data_t {
/// Return the variable set used for e.g. command duration. /// Return the variable set used for e.g. command duration.
env_stack_t &vars() { return parser_t::principal_parser().vars(); } 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(); } const env_stack_t &vars() const { return parser_t::principal_parser().vars(); }
/// Constructor /// Constructor
@ -829,7 +832,8 @@ void reader_write_title(const wcstring &cmd, bool reset_cursor_position) {
wcstring_list_t lst; wcstring_list_t lst;
proc_push_interactive(0); 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()) { !lst.empty()) {
fputws(L"\x1B]0;", stdout); fputws(L"\x1B]0;", stdout);
for (size_t i = 0; i < lst.size(); i++) { 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). // Prepend any mode indicator to the left prompt (issue #1988).
if (function_exists(MODE_PROMPT_FUNCTION_NAME)) { if (function_exists(MODE_PROMPT_FUNCTION_NAME)) {
wcstring_list_t mode_indicator_list; 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 // We do not support multiple lines in the mode indicator, so just concatenate all of
// them. // them.
for (size_t i = 0; i < mode_indicator_list.size(); i++) { for (size_t i = 0; i < mode_indicator_list.size(); i++) {
@ -878,7 +883,7 @@ static void exec_prompt() {
if (!data->left_prompt.empty()) { if (!data->left_prompt.empty()) {
wcstring_list_t prompt_list; wcstring_list_t prompt_list;
// Ignore return status. // 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++) { for (size_t i = 0; i < prompt_list.size(); i++) {
if (i > 0) data->left_prompt_buff += L'\n'; if (i > 0) data->left_prompt_buff += L'\n';
data->left_prompt_buff += prompt_list.at(i); data->left_prompt_buff += prompt_list.at(i);
@ -888,7 +893,7 @@ static void exec_prompt() {
if (!data->right_prompt.empty()) { if (!data->right_prompt.empty()) {
wcstring_list_t prompt_list; wcstring_list_t prompt_list;
// Status is ignored. // 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++) { for (size_t i = 0; i < prompt_list.size(); i++) {
// Right prompt does not support multiple lines, so just concatenate all of them. // Right prompt does not support multiple lines, so just concatenate all of them.
data->right_prompt_buff += prompt_list.at(i); 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 // 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 // (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. // 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"); wcstring path = (var ? var->as_string() : L"~/.bash_history");
expand_tilde(path); expand_tilde(path, data->vars());
FILE *f = wfopen(path, "r"); FILE *f = wfopen(path, "r");
if (f) { if (f) {
data->history->populate_from_bash(f); data->history->populate_from_bash(f);