diff options
author | 2016-12-21 11:25:50 +0000 | |
---|---|---|
committer | 2016-12-21 12:59:36 +0000 | |
commit | 108a6d5194fd0ac69eac7dceb9b7c3c8108e2180 (patch) | |
tree | f87f45e71ab65ea41c806addaa8ae0fa74448b6a /src | |
parent | e9c123a0b45c29f0cf9138a8398a160cd9d9ad77 (diff) |
Bazel client; implement NormalizePath
This method can normalize paths with "." and ".."
and multiple "/" characters.
E.g. normalize("../foo/./bar/../baz") = "foo/baz"
This method enables us implementing PathExists on
Windows. If the path to check is too long, we need
to prefix it with "\\?\" for the Windows API
functions to work, but then the path must be
fully normalized and in Windows format. We already
have functions to convert a path to Windows format
but that doesn't normalize; with this function we
can finally convert paths like "/c/foo/../bar" to
L"\\?\c:\foo" and check if it exists.
See https://github.com/bazelbuild/bazel/issues/2107
See https://github.com/bazelbuild/bazel/issues/2181
--
PiperOrigin-RevId: 142648194
MOS_MIGRATED_REVID=142648194
Diffstat (limited to 'src')
-rw-r--r-- | src/main/cpp/util/file.cc | 56 | ||||
-rw-r--r-- | src/main/cpp/util/file.h | 9 | ||||
-rw-r--r-- | src/test/cpp/util/file_test.cc | 16 |
3 files changed, 81 insertions, 0 deletions
diff --git a/src/main/cpp/util/file.cc b/src/main/cpp/util/file.cc index 392af372af..8d66710813 100644 --- a/src/main/cpp/util/file.cc +++ b/src/main/cpp/util/file.cc @@ -18,6 +18,7 @@ #include <algorithm> #include <cstdlib> +#include <sstream> // ostringstream #include <vector> #include "src/main/cpp/util/file_platform.h" @@ -31,6 +32,61 @@ using std::pair; using std::string; using std::vector; +string NormalizePath(const string &path) { + if (path.empty()) { + return string(); + } + + static const string dot("."); + static const string dotdot(".."); + + vector<string> segments; + int segment_start = -1; + // Find the path segments in `path` (separated by "/"). + for (int i = 0;; ++i) { + if (path[i] != '/' && path[i] != '\0') { + // The current character does not end a segment, so start one unless it's + // already started. + if (segment_start < 0) { + segment_start = i; + } + } else if (segment_start >= 0 && i > segment_start) { + // The current character is "/" or "\0", so this ends a segment. + // Add that to `segments` if there's anything to add; handle "." and "..". + string segment(path, segment_start, i - segment_start); + segment_start = -1; + if (segment == dotdot) { + if (!segments.empty()) { + segments.pop_back(); + } + } else if (segment != dot) { + segments.push_back(segment); + } + } + if (path[i] == '\0') { + break; + } + } + + // Handle the case when `path` was just "/" (or some degenerate form of it, + // e.g. "/.."). + if (segments.empty() && path[0] == '/') { + return "/"; + } + + // Join all segments, make sure we preserve the leading "/" if any. + bool first = true; + std::ostringstream result; + for (const auto &s : segments) { + if (!first || path[0] == '/') { + result << "/"; + } + first = false; + result << s; + } + return result.str(); +} + bool ReadFrom(const std::function<int(void *, int)> &read_func, string *content, int max_size) { content->clear(); diff --git a/src/main/cpp/util/file.h b/src/main/cpp/util/file.h index efe963c6d7..d380c33a0a 100644 --- a/src/main/cpp/util/file.h +++ b/src/main/cpp/util/file.h @@ -34,6 +34,15 @@ class IPipe { virtual int Receive(void *buffer, int size) = 0; }; +// Returns a normalized form of the input `path`. +// Normalization means removing "." references, resolving ".." references, and +// deduplicating "/" characters. +// For example if `path` is "foo/../bar/.//qux", the result is "bar/qux". +// Uplevel references that cannot go any higher in the directory tree are simply +// ignored, e.g. "/.." is normalized to "/" and "../../foo" is normalized to +// "foo". +std::string NormalizePath(const std::string &path); + // Replaces 'content' with data read from a source using `read_func`. // If `max_size` is positive, the method reads at most that many bytes; // otherwise the method reads everything. diff --git a/src/test/cpp/util/file_test.cc b/src/test/cpp/util/file_test.cc index fa379f567d..f98ed9975a 100644 --- a/src/test/cpp/util/file_test.cc +++ b/src/test/cpp/util/file_test.cc @@ -22,6 +22,22 @@ namespace blaze_util { +TEST(FileTest, TestNormalizePath) { + ASSERT_EQ(string(""), NormalizePath("")); + ASSERT_EQ(string(""), NormalizePath(".")); + ASSERT_EQ(string("/"), NormalizePath("/")); + ASSERT_EQ(string("/"), NormalizePath("//")); + ASSERT_EQ(string("foo"), NormalizePath("foo")); + ASSERT_EQ(string("foo"), NormalizePath("foo/")); + ASSERT_EQ(string("foo/bar"), NormalizePath("foo//bar")); + ASSERT_EQ(string("foo/bar"), NormalizePath("../..//foo//bar")); + ASSERT_EQ(string("/foo"), NormalizePath("/foo")); + ASSERT_EQ(string("/foo"), NormalizePath("/foo/")); + ASSERT_EQ(string("/foo/bar"), NormalizePath("/foo/./bar/")); + ASSERT_EQ(string("foo/bar"), NormalizePath("../foo/baz/../bar")); + ASSERT_EQ(string("foo/bar"), NormalizePath("../foo//./baz/../bar///")); +} + TEST(FileTest, TestSingleThreadedPipe) { std::unique_ptr<IPipe> pipe(CreatePipe()); char buffer[50] = {0}; |