Index: include/llvm/Support/Path.h =================================================================== --- include/llvm/Support/Path.h +++ include/llvm/Support/Path.h @@ -180,6 +180,18 @@ /// @param path A path that is transformed to native format. void native(SmallVectorImpl &path); +/// @brief Convert path to the canonical form in place. +/// +/// @code +/// /foo/./net/..//bar/ => /foo/bar +/// /foo/bar/net/ => /foo/bar/net +/// /foo/bar/../../net => /net +/// @endcode +/// +/// @param Path A path that is transformed to canonical format. +/// @param Result Holds the result of the transformation. +void canonical(StringRef path, SmallVectorImpl &Result); + /// @} /// @name Lexical Observers /// @{ Index: lib/Support/Path.cpp =================================================================== --- lib/Support/Path.cpp +++ lib/Support/Path.cpp @@ -531,6 +531,36 @@ #endif } +void canonical(StringRef Path, SmallVectorImpl &Result) { + Result.clear(); + SmallVector Components; + + // Split the path into the root and relative parts + // This is needed to ensure that the root path is just one component, + // as the path iterator can represent the root path using more than one + // component. + auto Root = root_path(Path); + auto Relative = Path.substr(Root.size()); + if (!Root.empty()) + Components.push_back(Root); + + for(auto I = begin(Relative), End = end(Relative); I != End; ++I) { + auto Component = *I; + if (Component == ".") + continue; + else if(Component == ".." && !Components.empty() && + Components.back() != "..") { + // Remove the previous component unless it's root + if (Components.back() != Root) + Components.pop_back(); + continue; + } + Components.push_back(Component); + } + for (const auto &Component : Components) + append(Result, Component); +} + const StringRef filename(StringRef path) { return *(--end(path)); } Index: unittests/Support/Path.cpp =================================================================== --- unittests/Support/Path.cpp +++ unittests/Support/Path.cpp @@ -144,6 +144,7 @@ EXPECT_EQ(*(--sys::path::end(filename)), (stem + ext).str()); path::native(*i, temp_store); + path::canonical(*i, temp_store); } } @@ -258,6 +259,32 @@ #endif } +TEST(Support, CanonicalPath) { + auto canonical = [] (StringRef Path) -> std::string { + SmallString<256> Result; + path::canonical(Path, Result); + return Result.str(); + }; +#ifdef LLVM_ON_WIN32 + EXPECT_EQ(canonical("..\\foo/."), "..\\foo"); + EXPECT_EQ(canonical("a:/foo\net/.."),"a:\\foo"); + EXPECT_EQ(canonical("d:\\foo/net\\bar"), "d:\\foo\\net\\bar"); + EXPECT_EQ(canonical("..\\\\./.."), "..\\.."); + EXPECT_EQ(canonical("a:\\\\foo\\.\\net/..\\\\bar\\"), "a:\\foo\\bar"); + EXPECT_EQ(canonical("\\\\net\\dir\\../bar"), "\\\\net\\bar"); + EXPECT_EQ(canonical("a:/.."), "a:\\"); +#else + EXPECT_EQ(canonical("/foo/./net/..//bar/"), "/foo/bar"); + EXPECT_EQ(canonical("foo/./bar/../../../net"), "../net"); + EXPECT_EQ(canonical("../foo/."), "../foo"); + EXPECT_EQ(canonical("..//./.."), "../.."); + EXPECT_EQ(canonical("/foo/./net/../../net"), "/net"); + EXPECT_EQ(canonical("/foo/bar/net/"), "/foo/bar/net"); + EXPECT_EQ(canonical("/foo/../net"), "/net"); + EXPECT_EQ(canonical("/.."), "/"); +#endif +} + class FileSystemTest : public testing::Test { protected: /// Unique temporary directory in which all created filesystem entities must