From a4d0ea406e8622e305fc3253075cfee60da3d3d2 Mon Sep 17 00:00:00 2001 From: Laszlo Csomor Date: Mon, 19 Dec 2016 14:30:52 +0000 Subject: Bazel client: SplitPath works with Windows paths This allows correct behavior of Dirname and Basename on Windows. See https://github.com/bazelbuild/bazel/issues/2107 -- PiperOrigin-RevId: 142441234 MOS_MIGRATED_REVID=142441234 --- src/main/cpp/util/file.cc | 12 --------- src/main/cpp/util/file_platform.h | 3 +++ src/main/cpp/util/file_posix.cc | 12 +++++++++ src/main/cpp/util/file_windows.cc | 51 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 12 deletions(-) (limited to 'src/main') diff --git a/src/main/cpp/util/file.cc b/src/main/cpp/util/file.cc index 2f6e6c66f2..392af372af 100644 --- a/src/main/cpp/util/file.cc +++ b/src/main/cpp/util/file.cc @@ -69,18 +69,6 @@ bool WriteFile(const std::string &content, const std::string &filename) { return WriteFile(content.c_str(), content.size(), filename); } -pair SplitPath(const string &path) { - size_t pos = path.rfind('/'); - - // Handle the case with no '/' in 'path'. - if (pos == string::npos) return std::make_pair("", path); - - // Handle the case with a single leading '/' in 'path'. - if (pos == 0) return std::make_pair(string(path, 0, 1), string(path, 1)); - - return std::make_pair(string(path, 0, pos), string(path, pos + 1)); -} - string Dirname(const string &path) { return SplitPath(path).first; } diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h index 1db579a494..78322dfeb8 100644 --- a/src/main/cpp/util/file_platform.h +++ b/src/main/cpp/util/file_platform.h @@ -26,6 +26,9 @@ class IPipe; IPipe* CreatePipe(); +// Split a path to dirname and basename parts. +std::pair SplitPath(const std::string &path); + // Replaces 'content' with contents of file 'filename'. // If `max_size` is positive, the method reads at most that many bytes; // otherwise the method reads the whole file. diff --git a/src/main/cpp/util/file_posix.cc b/src/main/cpp/util/file_posix.cc index e197a09453..9e835d759e 100644 --- a/src/main/cpp/util/file_posix.cc +++ b/src/main/cpp/util/file_posix.cc @@ -169,6 +169,18 @@ IPipe* CreatePipe() { } #endif // __CYGWIN__ +pair SplitPath(const string &path) { + size_t pos = path.rfind('/'); + + // Handle the case with no '/' in 'path'. + if (pos == string::npos) return std::make_pair("", path); + + // Handle the case with a single leading '/' in 'path'. + if (pos == 0) return std::make_pair(string(path, 0, 1), string(path, 1)); + + return std::make_pair(string(path, 0, pos), string(path, pos + 1)); +} + bool ReadFile(const string &filename, string *content, int max_size) { int fd = open(filename.c_str(), O_RDONLY); if (fd == -1) return false; diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc index 29153d1812..1d00269002 100644 --- a/src/main/cpp/util/file_windows.cc +++ b/src/main/cpp/util/file_windows.cc @@ -21,6 +21,7 @@ namespace blaze_util { +using std::pair; using std::string; class WindowsPipe : public IPipe { @@ -71,6 +72,56 @@ IPipe* CreatePipe() { return new WindowsPipe(read_handle, write_handle); } +static bool IsRootDirectory(const string& path) { + // Return true if path is "/", "\", "c:/", "c:\", "\\?\c:\", or "\??\c:\". + // + // It is unclear whether the UNC prefix is just "\\?\" or is "\??\" also + // valid (in some cases it seems to be, though MSDN doesn't mention it). + return + // path is "/" or "\" + (path.size() == 1 && (path[0] == '/' || path[0] == '\\')) || + // path is "c:/" or "c:\" + (path.size() == 3 && isalpha(path[0]) && path[1] == ':' && + (path[2] == '/' || path[2] == '\\')) || + // path is "\\?\c:\" or "\??\c:\" + (path.size() == 7 && path[0] == '\\' && + (path[1] == '\\' || path[1] == '?') && path[2] == '?' && + path[3] == '\\' && isalpha(path[4]) && path[5] == ':' && + path[6] == '\\'); +} + +pair SplitPath(const string& path) { + if (path.empty()) { + return std::make_pair("", ""); + } + + size_t pos = path.size() - 1; + for (auto it = path.crbegin(); it != path.crend(); ++it, --pos) { + if (*it == '/' || *it == '\\') { + if ((pos == 2 || pos == 6) && IsRootDirectory(path.substr(0, pos + 1))) { + // Windows path, top-level directory, e.g. "c:\foo", + // result is ("c:\", "foo"). + // Or UNC path, top-level directory, e.g. "\\?\c:\foo" + // result is ("\\?\c:\", "foo"). + return std::make_pair( + // Include the "/" or "\" in the drive specifier. + path.substr(0, pos + 1), path.substr(pos + 1)); + } else { + // Unix path, or relative path. + return std::make_pair( + // If the only "/" is the leading one, then that shall be the first + // pair element, otherwise the substring up to the rightmost "/". + pos == 0 ? path.substr(0, 1) : path.substr(0, pos), + // If the rightmost "/" is the tail, then the second pair element + // should be empty. + pos == path.size() - 1 ? "" : path.substr(pos + 1)); + } + } + } + // Handle the case with no '/' or '\' in `path`. + return std::make_pair("", path); +} + #ifdef COMPILER_MSVC bool ReadFile(const string& filename, string* content, int max_size) { // TODO(bazel-team): implement this. -- cgit v1.2.3