LCOV - code coverage report
Current view: top level - unit_tests/googletest/googletest/src - gtest-filepath.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 18.2 % 99 18
Test Date: 2025-07-11 13:54:12 Functions: 15.8 % 19 3

            Line data    Source code
       1              : // Copyright 2008, Google Inc.
       2              : // All rights reserved.
       3              : //
       4              : // Redistribution and use in source and binary forms, with or without
       5              : // modification, are permitted provided that the following conditions are
       6              : // met:
       7              : //
       8              : //     * Redistributions of source code must retain the above copyright
       9              : // notice, this list of conditions and the following disclaimer.
      10              : //     * Redistributions in binary form must reproduce the above
      11              : // copyright notice, this list of conditions and the following disclaimer
      12              : // in the documentation and/or other materials provided with the
      13              : // distribution.
      14              : //     * Neither the name of Google Inc. nor the names of its
      15              : // contributors may be used to endorse or promote products derived from
      16              : // this software without specific prior written permission.
      17              : //
      18              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      19              : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      20              : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      21              : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      22              : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      23              : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      24              : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      25              : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      26              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      27              : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      28              : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      29              : 
      30              : #include "gtest/internal/gtest-filepath.h"
      31              : 
      32              : #include <stdlib.h>
      33              : 
      34              : #include <iterator>
      35              : #include <string>
      36              : 
      37              : #include "gtest/gtest-message.h"
      38              : #include "gtest/internal/gtest-port.h"
      39              : 
      40              : #ifdef GTEST_OS_WINDOWS_MOBILE
      41              : #include <windows.h>
      42              : #elif defined(GTEST_OS_WINDOWS)
      43              : #include <direct.h>
      44              : #include <io.h>
      45              : #else
      46              : #include <limits.h>
      47              : 
      48              : #include <climits>  // Some Linux distributions define PATH_MAX here.
      49              : #endif              // GTEST_OS_WINDOWS_MOBILE
      50              : 
      51              : #include "gtest/internal/gtest-string.h"
      52              : 
      53              : #ifdef GTEST_OS_WINDOWS
      54              : #define GTEST_PATH_MAX_ _MAX_PATH
      55              : #elif defined(PATH_MAX)
      56              : #define GTEST_PATH_MAX_ PATH_MAX
      57              : #elif defined(_XOPEN_PATH_MAX)
      58              : #define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
      59              : #else
      60              : #define GTEST_PATH_MAX_ _POSIX_PATH_MAX
      61              : #endif  // GTEST_OS_WINDOWS
      62              : 
      63              : #if GTEST_HAS_FILE_SYSTEM
      64              : 
      65              : namespace testing {
      66              : namespace internal {
      67              : 
      68              : #ifdef GTEST_OS_WINDOWS
      69              : // On Windows, '\\' is the standard path separator, but many tools and the
      70              : // Windows API also accept '/' as an alternate path separator. Unless otherwise
      71              : // noted, a file path can contain either kind of path separators, or a mixture
      72              : // of them.
      73              : const char kPathSeparator = '\\';
      74              : const char kAlternatePathSeparator = '/';
      75              : const char kAlternatePathSeparatorString[] = "/";
      76              : #ifdef GTEST_OS_WINDOWS_MOBILE
      77              : // Windows CE doesn't have a current directory. You should not use
      78              : // the current directory in tests on Windows CE, but this at least
      79              : // provides a reasonable fallback.
      80              : const char kCurrentDirectoryString[] = "\\";
      81              : // Windows CE doesn't define INVALID_FILE_ATTRIBUTES
      82              : const DWORD kInvalidFileAttributes = 0xffffffff;
      83              : #else
      84              : const char kCurrentDirectoryString[] = ".\\";
      85              : #endif  // GTEST_OS_WINDOWS_MOBILE
      86              : #else
      87              : const char kPathSeparator = '/';
      88              : const char kCurrentDirectoryString[] = "./";
      89              : #endif  // GTEST_OS_WINDOWS
      90              : 
      91              : // Returns whether the given character is a valid path separator.
      92           42 : static bool IsPathSeparator(char c) {
      93              : #if GTEST_HAS_ALT_PATH_SEP_
      94              :   return (c == kPathSeparator) || (c == kAlternatePathSeparator);
      95              : #else
      96           42 :   return c == kPathSeparator;
      97              : #endif
      98              : }
      99              : 
     100              : // Returns the current working directory, or "" if unsuccessful.
     101            1 : FilePath FilePath::GetCurrentDir() {
     102              : #if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_PHONE) || \
     103              :     defined(GTEST_OS_WINDOWS_RT) || defined(GTEST_OS_ESP8266) ||           \
     104              :     defined(GTEST_OS_ESP32) || defined(GTEST_OS_XTENSA) ||                 \
     105              :     defined(GTEST_OS_QURT) || defined(GTEST_OS_NXP_QN9090) ||              \
     106              :     defined(GTEST_OS_NRF52)
     107              :   // These platforms do not have a current directory, so we just return
     108              :   // something reasonable.
     109              :   return FilePath(kCurrentDirectoryString);
     110              : #elif defined(GTEST_OS_WINDOWS)
     111              :   char cwd[GTEST_PATH_MAX_ + 1] = {'\0'};
     112              :   return FilePath(_getcwd(cwd, sizeof(cwd)) == nullptr ? "" : cwd);
     113              : #else
     114            1 :   char cwd[GTEST_PATH_MAX_ + 1] = {'\0'};
     115            1 :   char* result = getcwd(cwd, sizeof(cwd));
     116              : #ifdef GTEST_OS_NACL
     117              :   // getcwd will likely fail in NaCl due to the sandbox, so return something
     118              :   // reasonable. The user may have provided a shim implementation for getcwd,
     119              :   // however, so fallback only when failure is detected.
     120              :   return FilePath(result == nullptr ? kCurrentDirectoryString : cwd);
     121              : #endif  // GTEST_OS_NACL
     122            4 :   return FilePath(result == nullptr ? "" : cwd);
     123              : #endif  // GTEST_OS_WINDOWS_MOBILE
     124              : }
     125              : 
     126              : // Returns a copy of the FilePath with the case-insensitive extension removed.
     127              : // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
     128              : // FilePath("dir/file"). If a case-insensitive extension is not
     129              : // found, returns a copy of the original FilePath.
     130            0 : FilePath FilePath::RemoveExtension(const char* extension) const {
     131            0 :   const std::string dot_extension = std::string(".") + extension;
     132            0 :   if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) {
     133              :     return FilePath(
     134            0 :         pathname_.substr(0, pathname_.length() - dot_extension.length()));
     135              :   }
     136            0 :   return *this;
     137            0 : }
     138              : 
     139              : // Returns a pointer to the last occurrence of a valid path separator in
     140              : // the FilePath. On Windows, for example, both '/' and '\' are valid path
     141              : // separators. Returns NULL if no path separator was found.
     142            0 : const char* FilePath::FindLastPathSeparator() const {
     143            0 :   const char* const last_sep = strrchr(c_str(), kPathSeparator);
     144              : #if GTEST_HAS_ALT_PATH_SEP_
     145              :   const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
     146              :   // Comparing two pointers of which only one is NULL is undefined.
     147              :   if (last_alt_sep != nullptr &&
     148              :       (last_sep == nullptr || last_alt_sep > last_sep)) {
     149              :     return last_alt_sep;
     150              :   }
     151              : #endif
     152            0 :   return last_sep;
     153              : }
     154              : 
     155            0 : size_t FilePath::CalculateRootLength() const {
     156            0 :   const auto& path = pathname_;
     157            0 :   auto s = path.begin();
     158            0 :   auto end = path.end();
     159              : #ifdef GTEST_OS_WINDOWS
     160              :   if (end - s >= 2 && s[1] == ':' && (end - s == 2 || IsPathSeparator(s[2])) &&
     161              :       (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))) {
     162              :     // A typical absolute path like "C:\Windows" or "D:"
     163              :     s += 2;
     164              :     if (s != end) {
     165              :       ++s;
     166              :     }
     167              :   } else if (end - s >= 3 && IsPathSeparator(*s) && IsPathSeparator(*(s + 1)) &&
     168              :              !IsPathSeparator(*(s + 2))) {
     169              :     // Move past the "\\" prefix in a UNC path like "\\Server\Share\Folder"
     170              :     s += 2;
     171              :     // Skip 2 components and their following separators ("Server\" and "Share\")
     172              :     for (int i = 0; i < 2; ++i) {
     173              :       while (s != end) {
     174              :         bool stop = IsPathSeparator(*s);
     175              :         ++s;
     176              :         if (stop) {
     177              :           break;
     178              :         }
     179              :       }
     180              :     }
     181              :   } else if (s != end && IsPathSeparator(*s)) {
     182              :     // A drive-rooted path like "\Windows"
     183              :     ++s;
     184              :   }
     185              : #else
     186            0 :   if (s != end && IsPathSeparator(*s)) {
     187            0 :     ++s;
     188              :   }
     189              : #endif
     190            0 :   return static_cast<size_t>(s - path.begin());
     191              : }
     192              : 
     193              : // Returns a copy of the FilePath with the directory part removed.
     194              : // Example: FilePath("path/to/file").RemoveDirectoryName() returns
     195              : // FilePath("file"). If there is no directory part ("just_a_file"), it returns
     196              : // the FilePath unmodified. If there is no file part ("just_a_dir/") it
     197              : // returns an empty FilePath ("").
     198              : // On Windows platform, '\' is the path separator, otherwise it is '/'.
     199            0 : FilePath FilePath::RemoveDirectoryName() const {
     200            0 :   const char* const last_sep = FindLastPathSeparator();
     201            0 :   return last_sep ? FilePath(last_sep + 1) : *this;
     202              : }
     203              : 
     204              : // RemoveFileName returns the directory path with the filename removed.
     205              : // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
     206              : // If the FilePath is "a_file" or "/a_file", RemoveFileName returns
     207              : // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
     208              : // not have a file, like "just/a/dir/", it returns the FilePath unmodified.
     209              : // On Windows platform, '\' is the path separator, otherwise it is '/'.
     210            0 : FilePath FilePath::RemoveFileName() const {
     211            0 :   const char* const last_sep = FindLastPathSeparator();
     212            0 :   std::string dir;
     213            0 :   if (last_sep) {
     214            0 :     dir = std::string(c_str(), static_cast<size_t>(last_sep + 1 - c_str()));
     215              :   } else {
     216            0 :     dir = kCurrentDirectoryString;
     217              :   }
     218            0 :   return FilePath(dir);
     219            0 : }
     220              : 
     221              : // Helper functions for naming files in a directory for xml output.
     222              : 
     223              : // Given directory = "dir", base_name = "test", number = 0,
     224              : // extension = "xml", returns "dir/test.xml". If number is greater
     225              : // than zero (e.g., 12), returns "dir/test_12.xml".
     226              : // On Windows platform, uses \ as the separator rather than /.
     227            0 : FilePath FilePath::MakeFileName(const FilePath& directory,
     228              :                                 const FilePath& base_name, int number,
     229              :                                 const char* extension) {
     230            0 :   std::string file;
     231            0 :   if (number == 0) {
     232            0 :     file = base_name.string() + "." + extension;
     233              :   } else {
     234            0 :     file =
     235            0 :         base_name.string() + "_" + StreamableToString(number) + "." + extension;
     236              :   }
     237            0 :   return ConcatPaths(directory, FilePath(file));
     238            0 : }
     239              : 
     240              : // Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
     241              : // On Windows, uses \ as the separator rather than /.
     242            0 : FilePath FilePath::ConcatPaths(const FilePath& directory,
     243              :                                const FilePath& relative_path) {
     244            0 :   if (directory.IsEmpty()) return relative_path;
     245            0 :   const FilePath dir(directory.RemoveTrailingPathSeparator());
     246            0 :   return FilePath(dir.string() + kPathSeparator + relative_path.string());
     247            0 : }
     248              : 
     249              : // Returns true if pathname describes something findable in the file-system,
     250              : // either a file, directory, or whatever.
     251            0 : bool FilePath::FileOrDirectoryExists() const {
     252              : #ifdef GTEST_OS_WINDOWS_MOBILE
     253              :   LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
     254              :   const DWORD attributes = GetFileAttributes(unicode);
     255              :   delete[] unicode;
     256              :   return attributes != kInvalidFileAttributes;
     257              : #else
     258            0 :   posix::StatStruct file_stat{};
     259            0 :   return posix::Stat(pathname_.c_str(), &file_stat) == 0;
     260              : #endif  // GTEST_OS_WINDOWS_MOBILE
     261              : }
     262              : 
     263              : // Returns true if pathname describes a directory in the file-system
     264              : // that exists.
     265            0 : bool FilePath::DirectoryExists() const {
     266            0 :   bool result = false;
     267              : #ifdef GTEST_OS_WINDOWS
     268              :   // Don't strip off trailing separator if path is a root directory on
     269              :   // Windows (like "C:\\").
     270              :   const FilePath& path(IsRootDirectory() ? *this
     271              :                                          : RemoveTrailingPathSeparator());
     272              : #else
     273            0 :   const FilePath& path(*this);
     274              : #endif
     275              : 
     276              : #ifdef GTEST_OS_WINDOWS_MOBILE
     277              :   LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
     278              :   const DWORD attributes = GetFileAttributes(unicode);
     279              :   delete[] unicode;
     280              :   if ((attributes != kInvalidFileAttributes) &&
     281              :       (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
     282              :     result = true;
     283              :   }
     284              : #else
     285            0 :   posix::StatStruct file_stat{};
     286            0 :   result =
     287            0 :       posix::Stat(path.c_str(), &file_stat) == 0 && posix::IsDir(file_stat);
     288              : #endif  // GTEST_OS_WINDOWS_MOBILE
     289              : 
     290            0 :   return result;
     291              : }
     292              : 
     293              : // Returns true if pathname describes a root directory. (Windows has one
     294              : // root directory per disk drive. UNC share roots are also included.)
     295            0 : bool FilePath::IsRootDirectory() const {
     296            0 :   size_t root_length = CalculateRootLength();
     297            0 :   return root_length > 0 && root_length == pathname_.size() &&
     298            0 :          IsPathSeparator(pathname_[root_length - 1]);
     299              : }
     300              : 
     301              : // Returns true if pathname describes an absolute path.
     302            0 : bool FilePath::IsAbsolutePath() const { return CalculateRootLength() > 0; }
     303              : 
     304              : // Returns a pathname for a file that does not currently exist. The pathname
     305              : // will be directory/base_name.extension or
     306              : // directory/base_name_<number>.extension if directory/base_name.extension
     307              : // already exists. The number will be incremented until a pathname is found
     308              : // that does not already exist.
     309              : // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
     310              : // There could be a race condition if two or more processes are calling this
     311              : // function at the same time -- they could both pick the same filename.
     312            0 : FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
     313              :                                           const FilePath& base_name,
     314              :                                           const char* extension) {
     315            0 :   FilePath full_pathname;
     316            0 :   int number = 0;
     317              :   do {
     318            0 :     full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
     319            0 :   } while (full_pathname.FileOrDirectoryExists());
     320            0 :   return full_pathname;
     321            0 : }
     322              : 
     323              : // Returns true if FilePath ends with a path separator, which indicates that
     324              : // it is intended to represent a directory. Returns false otherwise.
     325              : // This does NOT check that a directory (or file) actually exists.
     326            0 : bool FilePath::IsDirectory() const {
     327            0 :   return !pathname_.empty() &&
     328            0 :          IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
     329              : }
     330              : 
     331              : // Create directories so that path exists. Returns true if successful or if
     332              : // the directories already exist; returns false if unable to create directories
     333              : // for any reason.
     334            0 : bool FilePath::CreateDirectoriesRecursively() const {
     335            0 :   if (!this->IsDirectory()) {
     336            0 :     return false;
     337              :   }
     338              : 
     339            0 :   if (pathname_.empty() || this->DirectoryExists()) {
     340            0 :     return true;
     341              :   }
     342              : 
     343            0 :   const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
     344            0 :   return parent.CreateDirectoriesRecursively() && this->CreateFolder();
     345            0 : }
     346              : 
     347              : // Create the directory so that path exists. Returns true if successful or
     348              : // if the directory already exists; returns false if unable to create the
     349              : // directory for any reason, including if the parent directory does not
     350              : // exist. Not named "CreateDirectory" because that's a macro on Windows.
     351            0 : bool FilePath::CreateFolder() const {
     352              : #ifdef GTEST_OS_WINDOWS_MOBILE
     353              :   FilePath removed_sep(this->RemoveTrailingPathSeparator());
     354              :   LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
     355              :   int result = CreateDirectory(unicode, nullptr) ? 0 : -1;
     356              :   delete[] unicode;
     357              : #elif defined(GTEST_OS_WINDOWS)
     358              :   int result = _mkdir(pathname_.c_str());
     359              : #elif defined(GTEST_OS_ESP8266) || defined(GTEST_OS_XTENSA) || \
     360              :     defined(GTEST_OS_QURT) || defined(GTEST_OS_NXP_QN9090) ||  \
     361              :     defined(GTEST_OS_NRF52)
     362              :   // do nothing
     363              :   int result = 0;
     364              : #else
     365            0 :   int result = mkdir(pathname_.c_str(), 0777);
     366              : #endif  // GTEST_OS_WINDOWS_MOBILE
     367              : 
     368            0 :   if (result == -1) {
     369            0 :     return this->DirectoryExists();  // An error is OK if the directory exists.
     370              :   }
     371            0 :   return true;  // No error.
     372              : }
     373              : 
     374              : // If input name has a trailing separator character, remove it and return the
     375              : // name, otherwise return the name string unmodified.
     376              : // On Windows platform, uses \ as the separator, other platforms use /.
     377            0 : FilePath FilePath::RemoveTrailingPathSeparator() const {
     378            0 :   return IsDirectory() ? FilePath(pathname_.substr(0, pathname_.length() - 1))
     379            0 :                        : *this;
     380              : }
     381              : 
     382              : // Removes any redundant separators that might be in the pathname.
     383              : // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
     384              : // redundancies that might be in a pathname involving "." or "..".
     385              : // Note that "\\Host\Share" does not contain a redundancy on Windows!
     386            1 : void FilePath::Normalize() {
     387            1 :   auto out = pathname_.begin();
     388              : 
     389            1 :   auto i = pathname_.cbegin();
     390              : #ifdef GTEST_OS_WINDOWS
     391              :   // UNC paths are treated specially
     392              :   if (pathname_.end() - i >= 3 && IsPathSeparator(*i) &&
     393              :       IsPathSeparator(*(i + 1)) && !IsPathSeparator(*(i + 2))) {
     394              :     *(out++) = kPathSeparator;
     395              :     *(out++) = kPathSeparator;
     396              :   }
     397              : #endif
     398           43 :   while (i != pathname_.end()) {
     399           42 :     const char character = *i;
     400           42 :     if (!IsPathSeparator(character)) {
     401           36 :       *(out++) = character;
     402           11 :     } else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) {
     403            6 :       *(out++) = kPathSeparator;
     404              :     }
     405           42 :     ++i;
     406              :   }
     407              : 
     408            1 :   pathname_.erase(out, pathname_.end());
     409            1 : }
     410              : 
     411              : }  // namespace internal
     412              : }  // namespace testing
     413              : 
     414              : #endif  // GTEST_HAS_FILE_SYSTEM
        

Generated by: LCOV version 2.0-1