aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/expand.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2016-04-07 15:24:52 -0700
committerGravatar Kurtis Rader <krader@skepticism.us>2016-04-07 20:15:32 -0700
commite395a0eb6927f298fcc7d0764ed27605941f34c0 (patch)
tree9f8bda281145b800128c246fbf84952a0dc8a191 /src/expand.cpp
parent36691df6fe4667645b71b550ce5eea90762ac23a (diff)
Migrate PATH-completion logic from complete.cpp to expand.cpp
Prior to this fix, when completing a command that doesn't have a /, we would prepend each component of PATH and then expand the whole thing. So any special characters in the PATH would be interpreted when performing tab completion. With this fix, we pull the PATH resolution out of complete.cpp and migrate it to expand.cpp. This unifies nicely with the CDPATH resolution already in that file. This requires introducing a new expand flag EXPAND_SPECIAL_FOR_COMMAND, which is analogous to EXPAND_SPECIAL_CD (which is renamed to EXPAND_SPECIAL_FOR_CD). This flag tells expand to resolve paths against PATH instead of the working directory. Fixes #952
Diffstat (limited to 'src/expand.cpp')
-rw-r--r--src/expand.cpp41
1 files changed, 26 insertions, 15 deletions
diff --git a/src/expand.cpp b/src/expand.cpp
index de67ffa8..cb68eb98 100644
--- a/src/expand.cpp
+++ b/src/expand.cpp
@@ -1800,36 +1800,47 @@ static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector<
const wcstring working_dir = env_get_pwd_slash();
wcstring_list_t effective_working_dirs;
- if (! (flags & EXPAND_SPECIAL_CD))
+ bool for_cd = !!(flags & EXPAND_SPECIAL_FOR_CD);
+ bool for_command = !!(flags & EXPAND_SPECIAL_FOR_COMMAND);
+ if (!for_cd && !for_command)
{
/* Common case */
effective_working_dirs.push_back(working_dir);
}
else
{
- /* Ignore the CDPATH if we start with ./ or / */
- if (string_prefixes_string(L"./", path_to_expand))
- {
- effective_working_dirs.push_back(working_dir);
- }
- else if (string_prefixes_string(L"/", path_to_expand))
+ /* Either EXPAND_SPECIAL_FOR_COMMAND or EXPAND_SPECIAL_FOR_CD. We can handle these mostly the same.
+ There's the following differences:
+ 1. An empty CDPATH should be treated as '.', but an empty PATH should be left empty (no commands can be found).
+ 2. PATH is only "one level," while CDPATH is multiple levels. That is, input like 'foo/bar' should resolve
+ against CDPATH, but not PATH.
+
+ In either case, we ignore the path if we start with ./ or /.
+ Also ignore it if we are doing command completion and we contain a slash, per IEEE 1003.1, chapter 8 under PATH
+ */
+ if (string_prefixes_string(L"./", path_to_expand) ||
+ string_prefixes_string(L"/", path_to_expand) ||
+ (for_command && path_to_expand.find(L'/') != wcstring::npos))
{
effective_working_dirs.push_back(working_dir);
}
else
{
- /* Get the CDPATH and cwd. Perhaps these should be passed in. */
- env_var_t cdpath = env_get_string(L"CDPATH");
- if (cdpath.missing_or_empty())
- cdpath = L".";
+ /* Get the PATH/CDPATH and cwd. Perhaps these should be passed in.
+ An empty CDPATH implies just the current directory, while an empty PATH is left empty.
+ */
+ env_var_t paths = env_get_string(for_cd ? L"CDPATH" : L"PATH");
+ if (paths.missing_or_empty())
+ paths = for_cd ? L"." : L"";
/* Tokenize it into directories */
- wcstokenizer tokenizer(cdpath, ARRAY_SEP_STR);
- wcstring next_cd_path;
- while (tokenizer.next(next_cd_path))
+ wcstokenizer tokenizer(paths, ARRAY_SEP_STR);
+ wcstring next_path;
+ while (tokenizer.next(next_path))
{
/* Ensure that we use the working directory for relative cdpaths like "." */
- effective_working_dirs.push_back(path_apply_working_directory(next_cd_path, working_dir));
+ effective_working_dirs.push_back(path_apply_working_directory(next_path, working_dir));
+
}
}
}