Add a fancy new paths_are_equivalent function to test for equivalent

paths instead of merely equal ones
This commit is contained in:
ridiculousfish 2013-08-27 18:26:22 -07:00
parent 85ce80d72e
commit 24f1da7f30
3 changed files with 61 additions and 16 deletions

View file

@ -766,9 +766,8 @@ static void test_path()
say(L"Testing path functions"); say(L"Testing path functions");
wcstring path = L"//foo//////bar/"; wcstring path = L"//foo//////bar/";
wcstring canon = path; path_make_canonical(path);
path_make_canonical(canon); if (path != L"/foo/bar")
if (canon != L"/foo/bar")
{ {
err(L"Bug in canonical PATH code"); err(L"Bug in canonical PATH code");
} }
@ -779,6 +778,11 @@ static void test_path()
{ {
err(L"Bug in canonical PATH code"); 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 enum word_motion_t

View file

@ -380,24 +380,62 @@ static void replace_all(wcstring &str, const wchar_t *needle, const wchar_t *rep
void path_make_canonical(wcstring &path) void path_make_canonical(wcstring &path)
{ {
// Ignore trailing slashes, unless it's the first character
size_t len = path.size();
while (len > 1 && path.at(len - 1) == L'/')
len--;
/* Remove double slashes */ // Turn runs of slashes into a single slash
size_t size; size_t trailing = 0;
do bool prev_was_slash = false;
for (size_t leading = 0; leading < len; leading++)
{ {
size = path.size(); wchar_t c = path.at(leading);
replace_all(path, L"//", L"/"); 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);
} }
while (path.size() != size);
/* Remove trailing slashes, except don't remove the first one */ bool paths_are_equivalent(const wcstring &p1, const wcstring &p2)
while (size-- > 1)
{ {
if (path.at(size) != L'/') 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)
{
wchar_t c1 = p1.at(idx1), c2 = p2.at(idx2);
// If the characters are different, the strings are not equivalent
if (c1 != c2)
break; 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) 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) bool paths_are_same_file(const wcstring &path1, const wcstring &path2)
{ {
if (path1 == path2) if (paths_are_equivalent(path1, path2))
return true; return true;
struct stat s1, s2; struct stat s1, s2;

3
path.h
View file

@ -73,6 +73,9 @@ bool path_can_be_implicit_cd(const wcstring &path,
*/ */
void path_make_canonical(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); bool path_is_valid(const wcstring &path, const wcstring &working_directory);
/** Returns whether the two paths refer to the same file */ /** Returns whether the two paths refer to the same file */