aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-08-27 18:26:22 -0700
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-08-27 18:26:22 -0700
commit24f1da7f309f7e7077e265e1777c9da46ff65e37 (patch)
tree25f6278f07e4a6c463e999bb4c3384f7f59e9fac
parent85ce80d72e666d51b52f8a54e8143062478286e4 (diff)
Add a fancy new paths_are_equivalent function to test for equivalent
paths instead of merely equal ones
-rw-r--r--fish_tests.cpp10
-rw-r--r--path.cpp66
-rw-r--r--path.h3
3 files changed, 62 insertions, 17 deletions
diff --git a/fish_tests.cpp b/fish_tests.cpp
index 41464c31..b47ce3a3 100644
--- a/fish_tests.cpp
+++ b/fish_tests.cpp
@@ -766,9 +766,8 @@ static void test_path()
say(L"Testing path functions");
wcstring path = L"//foo//////bar/";
- wcstring canon = path;
- path_make_canonical(canon);
- if (canon != L"/foo/bar")
+ path_make_canonical(path);
+ if (path != L"/foo/bar")
{
err(L"Bug in canonical PATH code");
}
@@ -779,6 +778,11 @@ static void test_path()
{
err(L"Bug in canonical PATH code");
}
+
+ if (paths_are_equivalent(L"/foo/bar/baz", L"foo/bar/baz")) err(L"Bug in canonical PATH code on line %ld", (long)__LINE__);
+ if (! paths_are_equivalent(L"///foo///bar/baz", L"/foo/bar////baz//")) err(L"Bug in canonical PATH code on line %ld", (long)__LINE__);
+ if (! paths_are_equivalent(L"/foo/bar/baz", L"/foo/bar/baz")) err(L"Bug in canonical PATH code on line %ld", (long)__LINE__);
+ if (! paths_are_equivalent(L"/", L"/")) err(L"Bug in canonical PATH code on line %ld", (long)__LINE__);
}
enum word_motion_t
diff --git a/path.cpp b/path.cpp
index 73cfc017..27aadbd3 100644
--- a/path.cpp
+++ b/path.cpp
@@ -380,24 +380,62 @@ static void replace_all(wcstring &str, const wchar_t *needle, const wchar_t *rep
void path_make_canonical(wcstring &path)
{
-
- /* Remove double slashes */
- size_t size;
- do
+ // Ignore trailing slashes, unless it's the first character
+ size_t len = path.size();
+ while (len > 1 && path.at(len - 1) == L'/')
+ len--;
+
+ // Turn runs of slashes into a single slash
+ size_t trailing = 0;
+ bool prev_was_slash = false;
+ for (size_t leading = 0; leading < len; leading++)
{
- size = path.size();
- replace_all(path, L"//", L"/");
- }
- while (path.size() != size);
+ wchar_t c = path.at(leading);
+ bool is_slash = (c == '/');
+ if (! prev_was_slash || ! is_slash)
+ {
+ // This is either the first slash in a run, or not a slash at all
+ path.at(trailing++) = c;
+ }
+ prev_was_slash = is_slash;
+ }
+ assert(trailing <= len);
+ if (trailing < len)
+ path.resize(trailing);
+}
- /* Remove trailing slashes, except don't remove the first one */
- while (size-- > 1)
+bool paths_are_equivalent(const wcstring &p1, const wcstring &p2)
+{
+ if (p1 == p2)
+ return true;
+
+ size_t len1 = p1.size(), len2 = p2.size();
+
+ // Ignore trailing slashes after the first character
+ while (len1 > 1 && p1.at(len1 - 1) == L'/') len1--;
+ while (len2 > 1 && p2.at(len2 - 1) == L'/') len2--;
+
+ // Start walking
+ size_t idx1 = 0, idx2 = 0;
+ while (idx1 < len1 && idx2 < len2)
{
- if (path.at(size) != L'/')
+ wchar_t c1 = p1.at(idx1), c2 = p2.at(idx2);
+
+ // If the characters are different, the strings are not equivalent
+ if (c1 != c2)
break;
+
+ idx1++;
+ idx2++;
+
+ // If the character was a slash, walk forwards until we hit the end of the string, or a non-slash
+ // Note the first condition is invariant within the loop
+ while (c1 == L'/' && idx1 < len1 && p1.at(idx1) == L'/') idx1++;
+ while (c2 == L'/' && idx2 < len2 && p2.at(idx2) == L'/') idx2++;
}
- /* Now size is either -1 (if the entire string was slashes) or is the index of the last non-slash character. Either way this will set it to the correct size. */
- path.resize(size+1);
+
+ // We matched if we consumed all of the characters in both strings
+ return idx1 == len1 && idx2 == len2;
}
bool path_is_valid(const wcstring &path, const wcstring &working_directory)
@@ -433,7 +471,7 @@ bool path_is_valid(const wcstring &path, const wcstring &working_directory)
bool paths_are_same_file(const wcstring &path1, const wcstring &path2)
{
- if (path1 == path2)
+ if (paths_are_equivalent(path1, path2))
return true;
struct stat s1, s2;
diff --git a/path.h b/path.h
index a566501e..b822f6e9 100644
--- a/path.h
+++ b/path.h
@@ -73,6 +73,9 @@ bool path_can_be_implicit_cd(const wcstring &path,
*/
void path_make_canonical(wcstring &path);
+/** Check if two paths are equivalent, which means to ignore runs of multiple slashes (or trailing slashes) */
+bool paths_are_equivalent(const wcstring &p1, const wcstring &p2);
+
bool path_is_valid(const wcstring &path, const wcstring &working_directory);
/** Returns whether the two paths refer to the same file */