Index: lib/Support/Path.cpp =================================================================== --- lib/Support/Path.cpp +++ lib/Support/Path.cpp @@ -90,10 +90,9 @@ return path.substr(0, end); } + // Returns the first character of the filename in str. For paths ending in + // '/', it returns the position of the '/'. size_t filename_pos(StringRef str, Style style) { - if (str.size() == 2 && is_separator(str[0], style) && str[0] == str[1]) - return 0; - if (str.size() > 0 && is_separator(str[str.size() - 1], style)) return str.size() - 1; @@ -110,6 +109,8 @@ return pos + 1; } + // Returns the position of the root directory in str. If there is no root + // directory in str, it returns StringRef::npos. size_t root_dir_start(StringRef str, Style style) { // case "c:/" if (real_style(style) == Style::windows) { @@ -117,10 +118,6 @@ return 2; } - // case "//" - if (str.size() == 2 && is_separator(str[0], style) && str[0] == str[1]) - return StringRef::npos; - // case "//net" if (str.size() > 3 && is_separator(str[0], style) && str[0] == str[1] && !is_separator(str[2], style)) { @@ -134,22 +131,29 @@ return StringRef::npos; } + // Returns the position past the end of the "parent path" of path. The parent + // path will not end in '/', unless the parent is the root directory. If the + // path has no parent, 0 is returned. size_t parent_path_end(StringRef path, Style style) { size_t end_pos = filename_pos(path, style); bool filename_was_sep = path.size() > 0 && is_separator(path[end_pos], style); - // Skip separators except for root dir. - size_t root_dir_pos = root_dir_start(path.substr(0, end_pos), style); - - while (end_pos > 0 && (end_pos - 1) != root_dir_pos && + // Skip separators until we reach root dir (or the start of the string). + size_t root_dir_pos = root_dir_start(path, style); + while (end_pos > 0 && + (root_dir_pos == StringRef::npos || end_pos > root_dir_pos) && is_separator(path[end_pos - 1], style)) --end_pos; - if (end_pos == 1 && root_dir_pos == 0 && filename_was_sep) - return StringRef::npos; + if (end_pos == root_dir_pos && !filename_was_sep) { + // We've reached the root dir and the input path was *not* ending in a + // sequence of slashes. Include the root dir in the parent path. + return root_dir_pos + 1; + } + // Otherwise, just include before the last slash. return end_pos; } } // end unnamed namespace @@ -281,8 +285,8 @@ ++Position; } - // Treat trailing '/' as a '.'. - if (Position == Path.size()) { + // Treat trailing '/' as a '.', unless it is the root dir. + if (Position == Path.size() && Component != "/") { --Position; Component = "."; return *this; @@ -321,23 +325,23 @@ } reverse_iterator &reverse_iterator::operator++() { - // If we're at the end and the previous char was a '/', return '.' unless - // we are the root path. size_t root_dir_pos = root_dir_start(Path, S); - if (Position == Path.size() && Path.size() > root_dir_pos + 1 && - is_separator(Path[Position - 1], S)) { - --Position; - Component = "."; - return *this; - } // Skip separators unless it's the root directory. size_t end_pos = Position; - while (end_pos > 0 && (end_pos - 1) != root_dir_pos && is_separator(Path[end_pos - 1], S)) --end_pos; + // Treat trailing '/' as a '.', unless it is the root dir. + if (Position == Path.size() && !Path.empty() && + is_separator(Path.back(), S) && + (root_dir_pos == StringRef::npos || end_pos - 1 > root_dir_pos)) { + --Position; + Component = "."; + return *this; + } + // Find next separator. size_t start_pos = filename_pos(Path.substr(0, end_pos), S); Component = Path.slice(start_pos, end_pos); Index: unittests/Support/Path.cpp =================================================================== --- unittests/Support/Path.cpp +++ unittests/Support/Path.cpp @@ -183,6 +183,15 @@ EXPECT_EQ("\\", path::filename("c:\\", path::Style::windows)); EXPECT_EQ("c:", path::parent_path("c:\\", path::Style::windows)); + EXPECT_EQ("/", path::filename("//")); + EXPECT_EQ("", path::parent_path("//")); + + EXPECT_EQ("/", path::filename("///")); + EXPECT_EQ("", path::parent_path("///")); + + EXPECT_EQ("\\", path::filename("c:\\\\", path::Style::windows)); + EXPECT_EQ("c:", path::parent_path("c:\\\\", path::Style::windows)); + EXPECT_EQ("bar", path::filename("/foo/bar")); EXPECT_EQ("/foo", path::parent_path("/foo/bar")); @@ -213,6 +222,8 @@ TEST(Support, PathIterator) { EXPECT_THAT(GetComponents("/foo"), testing::ElementsAre("/", "foo")); EXPECT_THAT(GetComponents("/"), testing::ElementsAre("/")); + EXPECT_THAT(GetComponents("//"), testing::ElementsAre("/")); + EXPECT_THAT(GetComponents("///"), testing::ElementsAre("/")); EXPECT_THAT(GetComponents("c/d/e/foo.txt"), testing::ElementsAre("c", "d", "e", "foo.txt")); EXPECT_THAT(GetComponents(".c/.d/../."), @@ -233,10 +244,8 @@ SmallVector, 4> Paths; Paths.emplace_back("/foo/", path::Style::native); Paths.emplace_back("/foo//", path::Style::native); - Paths.emplace_back("//net//", path::Style::native); Paths.emplace_back("//net/foo/", path::Style::native); Paths.emplace_back("c:\\foo\\", path::Style::windows); - Paths.emplace_back("c:\\\\", path::Style::windows); for (auto &Path : Paths) { SCOPED_TRACE(Path.first); @@ -248,6 +257,8 @@ RootPaths.emplace_back("/", path::Style::native); RootPaths.emplace_back("//net/", path::Style::native); RootPaths.emplace_back("c:\\", path::Style::windows); + RootPaths.emplace_back("//net//", path::Style::native); + RootPaths.emplace_back("c:\\\\", path::Style::windows); for (auto &Path : RootPaths) { SCOPED_TRACE(Path.first);