aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--expand.h5
-rw-r--r--fish_tests.cpp28
-rw-r--r--wildcard.cpp59
-rw-r--r--wildcard.h4
4 files changed, 76 insertions, 20 deletions
diff --git a/expand.h b/expand.h
index 14a4f477..fe13a145 100644
--- a/expand.h
+++ b/expand.h
@@ -60,7 +60,10 @@ enum
EXPAND_SKIP_HOME_DIRECTORIES = 1 << 9,
/** Allow fuzzy matching */
- EXPAND_FUZZY_MATCH = 1 << 10
+ EXPAND_FUZZY_MATCH = 1 << 10,
+
+ /** Requests that flag-like files not be sanitized. Sanitization means that a completion '--foo' that represents a file will be replaced by './--foo'. */
+ EXPAND_NO_SANITIZE_FLAGLIKE_FILES = 1 << 11
};
typedef int expand_flags_t;
diff --git a/fish_tests.cpp b/fish_tests.cpp
index 8d30d5f4..85327cca 100644
--- a/fish_tests.cpp
+++ b/fish_tests.cpp
@@ -1387,6 +1387,8 @@ static void test_expand()
if (system("mkdir -p /tmp/fish_expand_test/")) err(L"mkdir failed");
if (system("touch /tmp/fish_expand_test/.foo")) err(L"touch failed");
if (system("touch /tmp/fish_expand_test/bar")) err(L"touch failed");
+ if (system("touch /tmp/fish_expand_test/-flag1")) err(L"touch failed");
+ if (system("touch /tmp/fish_expand_test/--flag2")) err(L"touch failed");
// This is checking that .* does NOT match . and .. (https://github.com/fish-shell/fish-shell/issues/270). But it does have to match literal components (e.g. "./*" has to match the same as "*"
if (! expand_test(L"/tmp/fish_expand_test/.*", 0, L"/tmp/fish_expand_test/.foo", 0))
@@ -1397,6 +1399,32 @@ static void test_expand()
{
err(L"Expansion not correctly handling literal path components in dotfiles");
}
+ if (! expand_test(L"/tmp/fish_expand_test/*flag?", 0, L"/tmp/fish_expand_test/--flag2", L"/tmp/fish_expand_test/-flag1", 0))
+ {
+ err(L"Expansion not correctly handling flag-like files");
+ }
+
+ // Verify that flag-like file expansions never expand to flags
+ char saved_wd[PATH_MAX + 1] = {};
+ getcwd(saved_wd, sizeof saved_wd);
+ if (chdir("/tmp/fish_expand_test/")) err(L"chdir failed");
+ if (! expand_test(L"*flag?", 0, L"./--flag2", L"./-flag1", 0))
+ {
+ err(L"Expansion not correctly handling flag-like files in cwd");
+ }
+ if (! expand_test(L"*flag?", EXPAND_NO_SANITIZE_FLAGLIKE_FILES, L"--flag2", L"-flag1", 0))
+ {
+ err(L"Expansion not correctly handling flag-like files in cwd");
+ }
+
+
+ // For suffix-only completions, we don't attempt any sanitization
+ if (! expand_test(L"*flag", ACCEPT_INCOMPLETE, L"2", L"1", 0))
+ {
+ err(L"Expansion not correctly handling flag-like files in cwd");
+ }
+
+ if (chdir(saved_wd)) err(L"chdir restoration failed");
if (system("rm -Rf /tmp/fish_expand_test")) err(L"rm failed");
}
diff --git a/wildcard.cpp b/wildcard.cpp
index 7814a53f..1aa930fc 100644
--- a/wildcard.cpp
+++ b/wildcard.cpp
@@ -306,7 +306,15 @@ static bool wildcard_complete_internal(const wcstring &orig,
}
/* Note: out_completion may be empty if the completion really is empty, e.g. tab-completing 'foo' when a file 'foo' exists. */
- append_completion(out, out_completion, out_desc, flags, fuzzy_match);
+ if ((expand_flags & EXPAND_NO_SANITIZE_FLAGLIKE_FILES) == 0 && string_prefixes_string(L"-", out_completion))
+ {
+ append_completion(out, L"./" + out_completion, out_desc, flags, fuzzy_match);
+ }
+ else
+ {
+ append_completion(out, out_completion, out_desc, flags, fuzzy_match);
+ }
+
return true;
}
@@ -629,7 +637,7 @@ static void wildcard_completion_allocate(std::vector<completion_t> &list,
wcstring sb;
wcstring munged_completion;
- int flags = 0;
+ complete_flags_t complete_flags = 0;
int stat_res, lstat_res;
int stat_errno=0;
@@ -683,7 +691,7 @@ static void wildcard_completion_allocate(std::vector<completion_t> &list,
if (sz >= 0 && S_ISDIR(buf.st_mode))
{
- flags |= COMPLETE_NO_SPACE;
+ complete_flags |= COMPLETE_NO_SPACE;
munged_completion = completion;
munged_completion.push_back(L'/');
if (wants_desc)
@@ -702,8 +710,14 @@ static void wildcard_completion_allocate(std::vector<completion_t> &list,
}
}
+ /* Hackish. If the fullname is longer than the completion, it means we have a prefix. An example is completing "./foo" where the fullname is "./foo" and the completion might be "foo". In that case, we will pass "foo" to wildcard_complete, and it may choose to sanitize the file by appending a ./ to the front (since it doesn't know it already has one). Avoid that by cleaing the sanitize flag. */
+ if (completion.size() < fullname.size())
+ {
+ expand_flags |= EXPAND_NO_SANITIZE_FLAGLIKE_FILES;
+ }
+
const wcstring &completion_to_use = munged_completion.empty() ? completion : munged_completion;
- wildcard_complete(completion_to_use, wc, sb.c_str(), NULL, list, expand_flags, flags);
+ wildcard_complete(completion_to_use, wc, sb.c_str(), NULL, list, expand_flags, complete_flags);
}
/**
@@ -711,8 +725,9 @@ static void wildcard_completion_allocate(std::vector<completion_t> &list,
expansion flags specified. flags can be a combination of
EXECUTABLES_ONLY and DIRECTORIES_ONLY.
*/
-static bool test_flags(const wchar_t *filename, expand_flags_t flags)
+static bool test_flags(const wcstring &filename_str, expand_flags_t flags)
{
+ const wchar_t * const filename = filename_str.c_str();
if (flags & DIRECTORIES_ONLY)
{
struct stat buf;
@@ -747,11 +762,22 @@ static bool test_flags(const wchar_t *filename, expand_flags_t flags)
return true;
}
-/** Appends a completion to the completion list, if the string is missing from the set. */
-static void insert_completion_if_missing(const wcstring &str, std::vector<completion_t> &out, std::set<wcstring> &completion_set)
+/** Appends a completion to the completion list, if the string is missing from the set. Sanitizing means that if the file looks like a flag (has a leading -), we prepend a ./ */
+static void insert_and_sanitize_completion_if_missing(const wcstring &str, std::vector<completion_t> &out, expand_flags_t flags, std::set<wcstring> &completion_set)
{
if (completion_set.insert(str).second)
- append_completion(out, str);
+ {
+ if ((flags & EXPAND_NO_SANITIZE_FLAGLIKE_FILES) == 0 && string_prefixes_string(L"-", str))
+ {
+ // sanitized
+ append_completion(out, L"./" + str);
+ }
+ else
+ {
+ // not sanitized
+ append_completion(out, str);
+ }
+ }
}
/**
@@ -857,11 +883,11 @@ static int wildcard_expand_internal(const wchar_t *wc,
wcstring next;
while (wreaddir(dir, next))
{
- if (next[0] != L'.')
+ if (! next.empty() && next.at(0) != L'.')
{
wcstring long_name = make_path(base_dir, next);
- if (test_flags(long_name.c_str(), flags))
+ if (test_flags(long_name, flags))
{
wildcard_completion_allocate(out, long_name, next, L"", flags);
}
@@ -871,7 +897,7 @@ static int wildcard_expand_internal(const wchar_t *wc,
else
{
res = 1;
- insert_completion_if_missing(base_dir, out, completion_set);
+ insert_and_sanitize_completion_if_missing(base_dir, out, flags, completion_set);
}
}
else
@@ -889,7 +915,7 @@ static int wildcard_expand_internal(const wchar_t *wc,
std::vector<completion_t> test;
if (wildcard_complete(name_str, wc, L"", NULL, test, flags & EXPAND_FUZZY_MATCH, 0))
{
- if (test_flags(long_name.c_str(), flags))
+ if (test_flags(long_name, flags))
{
wildcard_completion_allocate(out, long_name, name_str, wc, flags);
@@ -907,7 +933,7 @@ static int wildcard_expand_internal(const wchar_t *wc,
{
/*
In recursive mode, we are only
- interested in adding files -directories
+ interested in adding files. Directories
will be added in the next pass.
*/
struct stat buf;
@@ -918,7 +944,7 @@ static int wildcard_expand_internal(const wchar_t *wc,
}
if (! skip)
{
- insert_completion_if_missing(long_name, out, completion_set);
+ insert_and_sanitize_completion_if_missing(long_name, out, flags, completion_set);
}
res = 1;
}
@@ -1060,10 +1086,7 @@ static int wildcard_expand_internal(const wchar_t *wc,
}
-int wildcard_expand(const wchar_t *wc,
- const wchar_t *base_dir,
- expand_flags_t flags,
- std::vector<completion_t> &out)
+static int wildcard_expand(const wchar_t *wc, const wchar_t *base_dir, expand_flags_t flags, std::vector<completion_t> &out)
{
size_t c = out.size();
diff --git a/wildcard.h b/wildcard.h
index 8c542221..b09aa64f 100644
--- a/wildcard.h
+++ b/wildcard.h
@@ -84,7 +84,9 @@ bool wildcard_has(const wcstring &, bool internal);
bool wildcard_has(const wchar_t *, bool internal);
/**
- Test wildcard completion
+ Matches the string against the wildcard, and if the wildcard is a
+ possible completion of the string, the remainder of the string is
+ inserted into the out vector.
*/
bool wildcard_complete(const wcstring &str,
const wchar_t *wc,