diff options
author | ridiculousfish <corydoras@ridiculousfish.com> | 2016-04-07 15:24:52 -0700 |
---|---|---|
committer | Kurtis Rader <krader@skepticism.us> | 2016-04-07 20:15:32 -0700 |
commit | e395a0eb6927f298fcc7d0764ed27605941f34c0 (patch) | |
tree | 9f8bda281145b800128c246fbf84952a0dc8a191 /src/expand.cpp | |
parent | 36691df6fe4667645b71b550ce5eea90762ac23a (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.cpp | 41 |
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)); + } } } |