diff options
Diffstat (limited to 'googletest/src/gtest-filepath.cc')
-rw-r--r-- | googletest/src/gtest-filepath.cc | 193 |
1 files changed, 119 insertions, 74 deletions
diff --git a/googletest/src/gtest-filepath.cc b/googletest/src/gtest-filepath.cc index 9504f0f5..902d8c7f 100644 --- a/googletest/src/gtest-filepath.cc +++ b/googletest/src/gtest-filepath.cc @@ -30,35 +30,42 @@ #include "gtest/internal/gtest-filepath.h" #include <stdlib.h> -#include "gtest/internal/gtest-port.h" + +#include <iterator> +#include <string> + #include "gtest/gtest-message.h" +#include "gtest/internal/gtest-port.h" -#if GTEST_OS_WINDOWS_MOBILE -# include <windows.h> -#elif GTEST_OS_WINDOWS -# include <direct.h> -# include <io.h> +#ifdef GTEST_OS_WINDOWS_MOBILE +#include <windows.h> +#elif defined(GTEST_OS_WINDOWS) +#include <direct.h> +#include <io.h> #else -# include <limits.h> -# include <climits> // Some Linux distributions define PATH_MAX here. -#endif // GTEST_OS_WINDOWS_MOBILE +#include <limits.h> + +#include <climits> // Some Linux distributions define PATH_MAX here. +#endif // GTEST_OS_WINDOWS_MOBILE #include "gtest/internal/gtest-string.h" -#if GTEST_OS_WINDOWS -# define GTEST_PATH_MAX_ _MAX_PATH +#ifdef GTEST_OS_WINDOWS +#define GTEST_PATH_MAX_ _MAX_PATH #elif defined(PATH_MAX) -# define GTEST_PATH_MAX_ PATH_MAX +#define GTEST_PATH_MAX_ PATH_MAX #elif defined(_XOPEN_PATH_MAX) -# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +#define GTEST_PATH_MAX_ _XOPEN_PATH_MAX #else -# define GTEST_PATH_MAX_ _POSIX_PATH_MAX +#define GTEST_PATH_MAX_ _POSIX_PATH_MAX #endif // GTEST_OS_WINDOWS +#if GTEST_HAS_FILE_SYSTEM + namespace testing { namespace internal { -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS // On Windows, '\\' is the standard path separator, but many tools and the // Windows API also accept '/' as an alternate path separator. Unless otherwise // noted, a file path can contain either kind of path separators, or a mixture @@ -66,16 +73,16 @@ namespace internal { const char kPathSeparator = '\\'; const char kAlternatePathSeparator = '/'; const char kAlternatePathSeparatorString[] = "/"; -# if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE // Windows CE doesn't have a current directory. You should not use // the current directory in tests on Windows CE, but this at least // provides a reasonable fallback. const char kCurrentDirectoryString[] = "\\"; // Windows CE doesn't define INVALID_FILE_ATTRIBUTES const DWORD kInvalidFileAttributes = 0xffffffff; -# else +#else const char kCurrentDirectoryString[] = ".\\"; -# endif // GTEST_OS_WINDOWS_MOBILE +#endif // GTEST_OS_WINDOWS_MOBILE #else const char kPathSeparator = '/'; const char kCurrentDirectoryString[] = "./"; @@ -92,24 +99,26 @@ static bool IsPathSeparator(char c) { // Returns the current working directory, or "" if unsuccessful. FilePath FilePath::GetCurrentDir() { -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ - GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_ESP32 || \ - GTEST_OS_XTENSA +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_WINDOWS_RT) || defined(GTEST_OS_ESP8266) || \ + defined(GTEST_OS_ESP32) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT) || defined(GTEST_OS_NXP_QN9090) || \ + defined(GTEST_OS_NRF52) // These platforms do not have a current directory, so we just return // something reasonable. return FilePath(kCurrentDirectoryString); -#elif GTEST_OS_WINDOWS - char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; +#elif defined(GTEST_OS_WINDOWS) + char cwd[GTEST_PATH_MAX_ + 1] = {'\0'}; return FilePath(_getcwd(cwd, sizeof(cwd)) == nullptr ? "" : cwd); #else - char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + char cwd[GTEST_PATH_MAX_ + 1] = {'\0'}; char* result = getcwd(cwd, sizeof(cwd)); -# if GTEST_OS_NACL +#ifdef GTEST_OS_NACL // getcwd will likely fail in NaCl due to the sandbox, so return something // reasonable. The user may have provided a shim implementation for getcwd, // however, so fallback only when failure is detected. return FilePath(result == nullptr ? kCurrentDirectoryString : cwd); -# endif // GTEST_OS_NACL +#endif // GTEST_OS_NACL return FilePath(result == nullptr ? "" : cwd); #endif // GTEST_OS_WINDOWS_MOBILE } @@ -121,8 +130,8 @@ FilePath FilePath::GetCurrentDir() { FilePath FilePath::RemoveExtension(const char* extension) const { const std::string dot_extension = std::string(".") + extension; if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { - return FilePath(pathname_.substr( - 0, pathname_.length() - dot_extension.length())); + return FilePath( + pathname_.substr(0, pathname_.length() - dot_extension.length())); } return *this; } @@ -143,6 +152,44 @@ const char* FilePath::FindLastPathSeparator() const { return last_sep; } +size_t FilePath::CalculateRootLength() const { + const auto& path = pathname_; + auto s = path.begin(); + auto end = path.end(); +#ifdef GTEST_OS_WINDOWS + if (end - s >= 2 && s[1] == ':' && (end - s == 2 || IsPathSeparator(s[2])) && + (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))) { + // A typical absolute path like "C:\Windows" or "D:" + s += 2; + if (s != end) { + ++s; + } + } else if (end - s >= 3 && IsPathSeparator(*s) && IsPathSeparator(*(s + 1)) && + !IsPathSeparator(*(s + 2))) { + // Move past the "\\" prefix in a UNC path like "\\Server\Share\Folder" + s += 2; + // Skip 2 components and their following separators ("Server\" and "Share\") + for (int i = 0; i < 2; ++i) { + while (s != end) { + bool stop = IsPathSeparator(*s); + ++s; + if (stop) { + break; + } + } + } + } else if (s != end && IsPathSeparator(*s)) { + // A drive-rooted path like "\Windows" + ++s; + } +#else + if (s != end && IsPathSeparator(*s)) { + ++s; + } +#endif + return static_cast<size_t>(s - path.begin()); +} + // Returns a copy of the FilePath with the directory part removed. // Example: FilePath("path/to/file").RemoveDirectoryName() returns // FilePath("file"). If there is no directory part ("just_a_file"), it returns @@ -178,15 +225,14 @@ FilePath FilePath::RemoveFileName() const { // than zero (e.g., 12), returns "dir/test_12.xml". // On Windows platform, uses \ as the separator rather than /. FilePath FilePath::MakeFileName(const FilePath& directory, - const FilePath& base_name, - int number, + const FilePath& base_name, int number, const char* extension) { std::string file; if (number == 0) { file = base_name.string() + "." + extension; } else { - file = base_name.string() + "_" + StreamableToString(number) - + "." + extension; + file = + base_name.string() + "_" + StreamableToString(number) + "." + extension; } return ConcatPaths(directory, FilePath(file)); } @@ -195,8 +241,7 @@ FilePath FilePath::MakeFileName(const FilePath& directory, // On Windows, uses \ as the separator rather than /. FilePath FilePath::ConcatPaths(const FilePath& directory, const FilePath& relative_path) { - if (directory.IsEmpty()) - return relative_path; + if (directory.IsEmpty()) return relative_path; const FilePath dir(directory.RemoveTrailingPathSeparator()); return FilePath(dir.string() + kPathSeparator + relative_path.string()); } @@ -204,13 +249,13 @@ FilePath FilePath::ConcatPaths(const FilePath& directory, // Returns true if pathname describes something findable in the file-system, // either a file, directory, or whatever. bool FilePath::FileOrDirectoryExists() const { -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); const DWORD attributes = GetFileAttributes(unicode); - delete [] unicode; + delete[] unicode; return attributes != kInvalidFileAttributes; #else - posix::StatStruct file_stat; + posix::StatStruct file_stat{}; return posix::Stat(pathname_.c_str(), &file_stat) == 0; #endif // GTEST_OS_WINDOWS_MOBILE } @@ -219,55 +264,42 @@ bool FilePath::FileOrDirectoryExists() const { // that exists. bool FilePath::DirectoryExists() const { bool result = false; -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS // Don't strip off trailing separator if path is a root directory on // Windows (like "C:\\"). - const FilePath& path(IsRootDirectory() ? *this : - RemoveTrailingPathSeparator()); + const FilePath& path(IsRootDirectory() ? *this + : RemoveTrailingPathSeparator()); #else const FilePath& path(*this); #endif -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); const DWORD attributes = GetFileAttributes(unicode); - delete [] unicode; + delete[] unicode; if ((attributes != kInvalidFileAttributes) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) { result = true; } #else - posix::StatStruct file_stat; - result = posix::Stat(path.c_str(), &file_stat) == 0 && - posix::IsDir(file_stat); + posix::StatStruct file_stat{}; + result = + posix::Stat(path.c_str(), &file_stat) == 0 && posix::IsDir(file_stat); #endif // GTEST_OS_WINDOWS_MOBILE return result; } // Returns true if pathname describes a root directory. (Windows has one -// root directory per disk drive.) +// root directory per disk drive. UNC share roots are also included.) bool FilePath::IsRootDirectory() const { -#if GTEST_OS_WINDOWS - return pathname_.length() == 3 && IsAbsolutePath(); -#else - return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); -#endif + size_t root_length = CalculateRootLength(); + return root_length > 0 && root_length == pathname_.size() && + IsPathSeparator(pathname_[root_length - 1]); } // Returns true if pathname describes an absolute path. -bool FilePath::IsAbsolutePath() const { - const char* const name = pathname_.c_str(); -#if GTEST_OS_WINDOWS - return pathname_.length() >= 3 && - ((name[0] >= 'a' && name[0] <= 'z') || - (name[0] >= 'A' && name[0] <= 'Z')) && - name[1] == ':' && - IsPathSeparator(name[2]); -#else - return IsPathSeparator(name[0]); -#endif -} +bool FilePath::IsAbsolutePath() const { return CalculateRootLength() > 0; } // Returns a pathname for a file that does not currently exist. The pathname // will be directory/base_name.extension or @@ -304,7 +336,7 @@ bool FilePath::CreateDirectoriesRecursively() const { return false; } - if (pathname_.length() == 0 || this->DirectoryExists()) { + if (pathname_.empty() || this->DirectoryExists()) { return true; } @@ -317,14 +349,16 @@ bool FilePath::CreateDirectoriesRecursively() const { // directory for any reason, including if the parent directory does not // exist. Not named "CreateDirectory" because that's a macro on Windows. bool FilePath::CreateFolder() const { -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE FilePath removed_sep(this->RemoveTrailingPathSeparator()); LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); int result = CreateDirectory(unicode, nullptr) ? 0 : -1; - delete [] unicode; -#elif GTEST_OS_WINDOWS + delete[] unicode; +#elif defined(GTEST_OS_WINDOWS) int result = _mkdir(pathname_.c_str()); -#elif GTEST_OS_ESP8266 || GTEST_OS_XTENSA +#elif defined(GTEST_OS_ESP8266) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT) || defined(GTEST_OS_NXP_QN9090) || \ + defined(GTEST_OS_NRF52) // do nothing int result = 0; #else @@ -341,25 +375,34 @@ bool FilePath::CreateFolder() const { // name, otherwise return the name string unmodified. // On Windows platform, uses \ as the separator, other platforms use /. FilePath FilePath::RemoveTrailingPathSeparator() const { - return IsDirectory() - ? FilePath(pathname_.substr(0, pathname_.length() - 1)) - : *this; + return IsDirectory() ? FilePath(pathname_.substr(0, pathname_.length() - 1)) + : *this; } // Removes any redundant separators that might be in the pathname. // For example, "bar///foo" becomes "bar/foo". Does not eliminate other // redundancies that might be in a pathname involving "." or "..". +// Note that "\\Host\Share" does not contain a redundancy on Windows! void FilePath::Normalize() { auto out = pathname_.begin(); - for (const char character : pathname_) { + auto i = pathname_.cbegin(); +#ifdef GTEST_OS_WINDOWS + // UNC paths are treated specially + if (pathname_.end() - i >= 3 && IsPathSeparator(*i) && + IsPathSeparator(*(i + 1)) && !IsPathSeparator(*(i + 2))) { + *(out++) = kPathSeparator; + *(out++) = kPathSeparator; + } +#endif + while (i != pathname_.end()) { + const char character = *i; if (!IsPathSeparator(character)) { *(out++) = character; } else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) { *(out++) = kPathSeparator; - } else { - continue; } + ++i; } pathname_.erase(out, pathname_.end()); @@ -367,3 +410,5 @@ void FilePath::Normalize() { } // namespace internal } // namespace testing + +#endif // GTEST_HAS_FILE_SYSTEM |