diff --git a/llvm/include/llvm/Support/Path.h b/llvm/include/llvm/Support/Path.h --- a/llvm/include/llvm/Support/Path.h +++ b/llvm/include/llvm/Support/Path.h @@ -25,7 +25,7 @@ namespace sys { namespace path { -enum class Style { windows, posix, native }; +enum class Style { windows, windows_forward, posix, native }; /// @name Lexical Component Iterator /// @{ @@ -241,6 +241,13 @@ /// @param path A path that is transformed to native format. void native(SmallVectorImpl &path, Style style = Style::native); +/// Convert path to the native form in place. On Windows, where both forward and +/// backwards slashes are path separators, convert to the preferred form. +/// On other platforms, this does nothing. +/// +/// @param path A path that is transformed to preferred format. +void make_preferred(SmallVectorImpl &path, Style style = Style::native); + /// Replaces backslashes with slashes if Windows. /// /// @param path processed path diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp --- a/llvm/lib/Support/Path.cpp +++ b/llvm/lib/Support/Path.cpp @@ -37,15 +37,23 @@ using llvm::sys::path::Style; inline Style real_style(Style style) { + if (style == Style::native) { #ifdef _WIN32 - return (style == Style::posix) ? Style::posix : Style::windows; + return Style::windows; #else - return (style == Style::windows) ? Style::windows : Style::posix; + return Style::posix; #endif + } + return style; + } + + bool is_style_windows(Style style) { + style = real_style(style); + return style == Style::windows || style == Style::windows_forward; } inline const char *separators(Style style) { - if (real_style(style) == Style::windows) + if (is_style_windows(style)) return "\\/"; return "/"; } @@ -66,7 +74,7 @@ if (path.empty()) return path; - if (real_style(style) == Style::windows) { + if (is_style_windows(style)) { // C: if (path.size() >= 2 && std::isalpha(static_cast(path[0])) && path[1] == ':') @@ -98,7 +106,7 @@ size_t pos = str.find_last_of(separators(style), str.size() - 1); - if (real_style(style) == Style::windows) { + if (is_style_windows(style)) { if (pos == StringRef::npos) pos = str.find_last_of(':', str.size() - 2); } @@ -113,7 +121,7 @@ // directory in str, it returns StringRef::npos. size_t root_dir_start(StringRef str, Style style) { // case "c:/" - if (real_style(style) == Style::windows) { + if (is_style_windows(style)) { if (str.size() > 2 && str[1] == ':' && is_separator(str[2], style)) return 2; } @@ -259,7 +267,7 @@ // Root dir. if (was_net || // c:/ - (real_style(S) == Style::windows && Component.endswith(":"))) { + (is_style_windows(S) && Component.endswith(":"))) { Component = Path.substr(Position, 1); return *this; } @@ -348,7 +356,7 @@ if (b != e) { bool has_net = b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; - bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); + bool has_drive = is_style_windows(style) && b->endswith(":"); if (has_net || has_drive) { if ((++pos != e) && is_separator((*pos)[0], style)) { @@ -373,7 +381,7 @@ if (b != e) { bool has_net = b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; - bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); + bool has_drive = is_style_windows(style) && b->endswith(":"); if (has_net || has_drive) { // just {C:,//net}, return the first component. @@ -390,7 +398,7 @@ if (b != e) { bool has_net = b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; - bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); + bool has_drive = is_style_windows(style) && b->endswith(":"); if ((has_net || has_drive) && // {C:,//net}, skip to the next component. @@ -497,7 +505,7 @@ static bool starts_with(StringRef Path, StringRef Prefix, Style style = Style::native) { // Windows prefix matching : case and separator insensitive - if (real_style(style) == Style::windows) { + if (is_style_windows(style)) { if (Path.size() < Prefix.size()) return false; for (size_t I = 0, E = Prefix.size(); I != E; ++I) { @@ -548,8 +556,10 @@ void native(SmallVectorImpl &Path, Style style) { if (Path.empty()) return; - if (real_style(style) == Style::windows) { - std::replace(Path.begin(), Path.end(), '/', '\\'); + if (is_style_windows(style)) { + std::replace_if(Path.begin(), Path.end(), + std::bind(is_separator, std::placeholders::_1, style), + preferred_separator(style)); if (Path[0] == '~' && (Path.size() == 1 || is_separator(Path[1], style))) { SmallString<128> PathHome; home_directory(PathHome); @@ -561,8 +571,14 @@ } } +void make_preferred(SmallVectorImpl &path, Style style) { + if (!is_style_windows(style)) + return; + native(path, style); +} + std::string convert_to_slash(StringRef path, Style style) { - if (real_style(style) != Style::windows) + if (!is_style_windows(style)) return std::string(path); std::string s = path.str(); @@ -597,7 +613,7 @@ bool is_separator(char value, Style style) { if (value == '/') return true; - if (real_style(style) == Style::windows) + if (is_style_windows(style)) return value == '\\'; return false; } @@ -670,7 +686,7 @@ bool rootDir = has_root_directory(p, style); bool rootName = - (real_style(style) != Style::windows) || has_root_name(p, style); + !is_style_windows(style) || has_root_name(p, style); return rootDir && rootName; } @@ -684,7 +700,7 @@ if (!p.empty() && is_separator(p.front(), style)) return true; - if (real_style(style) == Style::windows) { + if (is_style_windows(style)) { // Handle drive letter pattern (a character followed by ':') on Windows. if (p.size() >= 2 && (p[0] && p[1] == ':')) return true; @@ -904,7 +920,7 @@ bool rootName = path::has_root_name(p); // Already absolute. - if ((rootName || real_style(Style::native) != Style::windows) && + if ((rootName || !is_style_windows(Style::native)) && rootDirectory) return; diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp --- a/llvm/unittests/Support/Path.cpp +++ b/llvm/unittests/Support/Path.cpp @@ -76,6 +76,7 @@ EXPECT_FALSE(path::is_separator(' ')); EXPECT_TRUE(path::is_separator('\\', path::Style::windows)); + EXPECT_TRUE(path::is_separator('\\', path::Style::windows_forward)); EXPECT_FALSE(path::is_separator('\\', path::Style::posix)); #ifdef _WIN32 @@ -363,6 +364,8 @@ testing::ElementsAre("/", ".c", ".d", "..", ".")); EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows), testing::ElementsAre("c:", "\\", "c", "e", "foo.txt")); + EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows_forward), + testing::ElementsAre("c:", "\\", "c", "e", "foo.txt")); EXPECT_THAT(GetComponents("//net/"), testing::ElementsAre("//net", "/")); EXPECT_THAT(GetComponents("//net/c/foo.txt"), testing::ElementsAre("//net", "/", "c", "foo.txt")); @@ -1405,10 +1408,25 @@ for (auto &T : Tests) { SmallString<64> Win(std::get<0>(T)); SmallString<64> Posix(Win); + SmallString<64> WinForward(Win); path::native(Win, path::Style::windows); path::native(Posix, path::Style::posix); + path::native(WinForward, path::Style::windows_forward); EXPECT_EQ(std::get<1>(T), Win); EXPECT_EQ(std::get<2>(T), Posix); + EXPECT_EQ(std::get<2>(T), WinForward); + } + + for (auto &T : Tests) { + SmallString<64> Win(std::get<0>(T)); + SmallString<64> Posix(Win); + SmallString<64> WinForward(Win); + path::make_preferred(Win, path::Style::windows); + path::make_preferred(Posix, path::Style::posix); + path::make_preferred(WinForward, path::Style::windows_forward); + EXPECT_EQ(std::get<1>(T), Win); + EXPECT_EQ(std::get<0>(T), Posix); // Posix remains unchanged here + EXPECT_EQ(std::get<2>(T), WinForward); } #if defined(_WIN32) @@ -1417,10 +1435,15 @@ const char *Path7a = "~/aaa"; SmallString<64> Path7(Path7a); - path::native(Path7); + path::native(Path7, path::Style::windows); EXPECT_TRUE(Path7.endswith("\\aaa")); EXPECT_TRUE(Path7.startswith(PathHome)); EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1)); + Path7 = Path7a; + path::native(Path7, path::Style::windows_forward); + EXPECT_TRUE(Path7.endswith("/aaa")); + EXPECT_TRUE(Path7.startswith(PathHome)); + EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1)); const char *Path8a = "~"; SmallString<64> Path8(Path8a); @@ -1434,7 +1457,7 @@ const char *Path10a = "aaa/~/b"; SmallString<64> Path10(Path10a); - path::native(Path10); + path::native(Path10, path::Style::windows); EXPECT_EQ(Path10, "aaa\\~\\b"); #endif }