Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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. // Authors: keith.ray@gmail.com (Keith Ray)
  31.  
  32. #include "gtest/gtest-message.h"
  33. #include "gtest/internal/gtest-filepath.h"
  34. #include "gtest/internal/gtest-port.h"
  35.  
  36. #include <stdlib.h>
  37.  
  38. #if GTEST_OS_WINDOWS_MOBILE
  39. # include <windows.h>
  40. #elif GTEST_OS_WINDOWS
  41. # include <direct.h>
  42. # include <io.h>
  43. #elif GTEST_OS_SYMBIAN
  44. // Symbian OpenC has PATH_MAX in sys/syslimits.h
  45. # include <sys/syslimits.h>
  46. #else
  47. # include <limits.h>
  48. # include <climits>  // Some Linux distributions define PATH_MAX here.
  49. #endif  // GTEST_OS_WINDOWS_MOBILE
  50.  
  51. #if GTEST_OS_WINDOWS
  52. # define GTEST_PATH_MAX_ _MAX_PATH
  53. #elif defined(PATH_MAX)
  54. # define GTEST_PATH_MAX_ PATH_MAX
  55. #elif defined(_XOPEN_PATH_MAX)
  56. # define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
  57. #else
  58. # define GTEST_PATH_MAX_ _POSIX_PATH_MAX
  59. #endif  // GTEST_OS_WINDOWS
  60.  
  61. #include "gtest/internal/gtest-string.h"
  62.  
  63. namespace testing {
  64. namespace internal {
  65.  
  66. #if GTEST_OS_WINDOWS
  67. // On Windows, '\\' is the standard path separator, but many tools and the
  68. // Windows API also accept '/' as an alternate path separator. Unless otherwise
  69. // noted, a file path can contain either kind of path separators, or a mixture
  70. // of them.
  71. const char kPathSeparator = '\\';
  72. const char kAlternatePathSeparator = '/';
  73. const char kPathSeparatorString[] = "\\";
  74. const char kAlternatePathSeparatorString[] = "/";
  75. # if GTEST_OS_WINDOWS_MOBILE
  76. // Windows CE doesn't have a current directory. You should not use
  77. // the current directory in tests on Windows CE, but this at least
  78. // provides a reasonable fallback.
  79. const char kCurrentDirectoryString[] = "\\";
  80. // Windows CE doesn't define INVALID_FILE_ATTRIBUTES
  81. const DWORD kInvalidFileAttributes = 0xffffffff;
  82. # else
  83. const char kCurrentDirectoryString[] = ".\\";
  84. # endif  // GTEST_OS_WINDOWS_MOBILE
  85. #else
  86. const char kPathSeparator = '/';
  87. const char kPathSeparatorString[] = "/";
  88. const char kCurrentDirectoryString[] = "./";
  89. #endif  // GTEST_OS_WINDOWS
  90.  
  91. // Returns whether the given character is a valid path separator.
  92. static bool IsPathSeparator(char c) {
  93. #if GTEST_HAS_ALT_PATH_SEP_
  94.   return (c == kPathSeparator) || (c == kAlternatePathSeparator);
  95. #else
  96.   return c == kPathSeparator;
  97. #endif
  98. }
  99.  
  100. // Returns the current working directory, or "" if unsuccessful.
  101. FilePath FilePath::GetCurrentDir() {
  102. #if GTEST_OS_WINDOWS_MOBILE
  103.   // Windows CE doesn't have a current directory, so we just return
  104.   // something reasonable.
  105.   return FilePath(kCurrentDirectoryString);
  106. #elif GTEST_OS_WINDOWS
  107.   char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
  108.   return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
  109. #else
  110.   char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
  111.   return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
  112. #endif  // GTEST_OS_WINDOWS_MOBILE
  113. }
  114.  
  115. // Returns a copy of the FilePath with the case-insensitive extension removed.
  116. // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
  117. // FilePath("dir/file"). If a case-insensitive extension is not
  118. // found, returns a copy of the original FilePath.
  119. FilePath FilePath::RemoveExtension(const char* extension) const {
  120.   const std::string dot_extension = std::string(".") + extension;
  121.   if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) {
  122.     return FilePath(pathname_.substr(
  123.         0, pathname_.length() - dot_extension.length()));
  124.   }
  125.   return *this;
  126. }
  127.  
  128. // Returns a pointer to the last occurrence of a valid path separator in
  129. // the FilePath. On Windows, for example, both '/' and '\' are valid path
  130. // separators. Returns NULL if no path separator was found.
  131. const char* FilePath::FindLastPathSeparator() const {
  132.   const char* const last_sep = strrchr(c_str(), kPathSeparator);
  133. #if GTEST_HAS_ALT_PATH_SEP_
  134.   const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
  135.   // Comparing two pointers of which only one is NULL is undefined.
  136.   if (last_alt_sep != NULL &&
  137.       (last_sep == NULL || last_alt_sep > last_sep)) {
  138.     return last_alt_sep;
  139.   }
  140. #endif
  141.   return last_sep;
  142. }
  143.  
  144. // Returns a copy of the FilePath with the directory part removed.
  145. // Example: FilePath("path/to/file").RemoveDirectoryName() returns
  146. // FilePath("file"). If there is no directory part ("just_a_file"), it returns
  147. // the FilePath unmodified. If there is no file part ("just_a_dir/") it
  148. // returns an empty FilePath ("").
  149. // On Windows platform, '\' is the path separator, otherwise it is '/'.
  150. FilePath FilePath::RemoveDirectoryName() const {
  151.   const char* const last_sep = FindLastPathSeparator();
  152.   return last_sep ? FilePath(last_sep + 1) : *this;
  153. }
  154.  
  155. // RemoveFileName returns the directory path with the filename removed.
  156. // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
  157. // If the FilePath is "a_file" or "/a_file", RemoveFileName returns
  158. // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
  159. // not have a file, like "just/a/dir/", it returns the FilePath unmodified.
  160. // On Windows platform, '\' is the path separator, otherwise it is '/'.
  161. FilePath FilePath::RemoveFileName() const {
  162.   const char* const last_sep = FindLastPathSeparator();
  163.   std::string dir;
  164.   if (last_sep) {
  165.     dir = std::string(c_str(), last_sep + 1 - c_str());
  166.   } else {
  167.     dir = kCurrentDirectoryString;
  168.   }
  169.   return FilePath(dir);
  170. }
  171.  
  172. // Helper functions for naming files in a directory for xml output.
  173.  
  174. // Given directory = "dir", base_name = "test", number = 0,
  175. // extension = "xml", returns "dir/test.xml". If number is greater
  176. // than zero (e.g., 12), returns "dir/test_12.xml".
  177. // On Windows platform, uses \ as the separator rather than /.
  178. FilePath FilePath::MakeFileName(const FilePath& directory,
  179.                                 const FilePath& base_name,
  180.                                 int number,
  181.                                 const char* extension) {
  182.   std::string file;
  183.   if (number == 0) {
  184.     file = base_name.string() + "." + extension;
  185.   } else {
  186.     file = base_name.string() + "_" + StreamableToString(number)
  187.         + "." + extension;
  188.   }
  189.   return ConcatPaths(directory, FilePath(file));
  190. }
  191.  
  192. // Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
  193. // On Windows, uses \ as the separator rather than /.
  194. FilePath FilePath::ConcatPaths(const FilePath& directory,
  195.                                const FilePath& relative_path) {
  196.   if (directory.IsEmpty())
  197.     return relative_path;
  198.   const FilePath dir(directory.RemoveTrailingPathSeparator());
  199.   return FilePath(dir.string() + kPathSeparator + relative_path.string());
  200. }
  201.  
  202. // Returns true if pathname describes something findable in the file-system,
  203. // either a file, directory, or whatever.
  204. bool FilePath::FileOrDirectoryExists() const {
  205. #if GTEST_OS_WINDOWS_MOBILE
  206.   LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
  207.   const DWORD attributes = GetFileAttributes(unicode);
  208.   delete [] unicode;
  209.   return attributes != kInvalidFileAttributes;
  210. #else
  211.   posix::StatStruct file_stat;
  212.   return posix::Stat(pathname_.c_str(), &file_stat) == 0;
  213. #endif  // GTEST_OS_WINDOWS_MOBILE
  214. }
  215.  
  216. // Returns true if pathname describes a directory in the file-system
  217. // that exists.
  218. bool FilePath::DirectoryExists() const {
  219.   bool result = false;
  220. #if GTEST_OS_WINDOWS
  221.   // Don't strip off trailing separator if path is a root directory on
  222.   // Windows (like "C:\\").
  223.   const FilePath& path(IsRootDirectory() ? *this :
  224.                                            RemoveTrailingPathSeparator());
  225. #else
  226.   const FilePath& path(*this);
  227. #endif
  228.  
  229. #if GTEST_OS_WINDOWS_MOBILE
  230.   LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
  231.   const DWORD attributes = GetFileAttributes(unicode);
  232.   delete [] unicode;
  233.   if ((attributes != kInvalidFileAttributes) &&
  234.       (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
  235.     result = true;
  236.   }
  237. #else
  238.   posix::StatStruct file_stat;
  239.   result = posix::Stat(path.c_str(), &file_stat) == 0 &&
  240.       posix::IsDir(file_stat);
  241. #endif  // GTEST_OS_WINDOWS_MOBILE
  242.  
  243.   return result;
  244. }
  245.  
  246. // Returns true if pathname describes a root directory. (Windows has one
  247. // root directory per disk drive.)
  248. bool FilePath::IsRootDirectory() const {
  249. #if GTEST_OS_WINDOWS
  250.   // TODO(wan@google.com): on Windows a network share like
  251.   // \\server\share can be a root directory, although it cannot be the
  252.   // current directory.  Handle this properly.
  253.   return pathname_.length() == 3 && IsAbsolutePath();
  254. #else
  255.   return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
  256. #endif
  257. }
  258.  
  259. // Returns true if pathname describes an absolute path.
  260. bool FilePath::IsAbsolutePath() const {
  261.   const char* const name = pathname_.c_str();
  262. #if GTEST_OS_WINDOWS
  263.   return pathname_.length() >= 3 &&
  264.      ((name[0] >= 'a' && name[0] <= 'z') ||
  265.       (name[0] >= 'A' && name[0] <= 'Z')) &&
  266.      name[1] == ':' &&
  267.      IsPathSeparator(name[2]);
  268. #else
  269.   return IsPathSeparator(name[0]);
  270. #endif
  271. }
  272.  
  273. // Returns a pathname for a file that does not currently exist. The pathname
  274. // will be directory/base_name.extension or
  275. // directory/base_name_<number>.extension if directory/base_name.extension
  276. // already exists. The number will be incremented until a pathname is found
  277. // that does not already exist.
  278. // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
  279. // There could be a race condition if two or more processes are calling this
  280. // function at the same time -- they could both pick the same filename.
  281. FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
  282.                                           const FilePath& base_name,
  283.                                           const char* extension) {
  284.   FilePath full_pathname;
  285.   int number = 0;
  286.   do {
  287.     full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
  288.   } while (full_pathname.FileOrDirectoryExists());
  289.   return full_pathname;
  290. }
  291.  
  292. // Returns true if FilePath ends with a path separator, which indicates that
  293. // it is intended to represent a directory. Returns false otherwise.
  294. // This does NOT check that a directory (or file) actually exists.
  295. bool FilePath::IsDirectory() const {
  296.   return !pathname_.empty() &&
  297.          IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
  298. }
  299.  
  300. // Create directories so that path exists. Returns true if successful or if
  301. // the directories already exist; returns false if unable to create directories
  302. // for any reason.
  303. bool FilePath::CreateDirectoriesRecursively() const {
  304.   if (!this->IsDirectory()) {
  305.     return false;
  306.   }
  307.  
  308.   if (pathname_.length() == 0 || this->DirectoryExists()) {
  309.     return true;
  310.   }
  311.  
  312.   const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
  313.   return parent.CreateDirectoriesRecursively() && this->CreateFolder();
  314. }
  315.  
  316. // Create the directory so that path exists. Returns true if successful or
  317. // if the directory already exists; returns false if unable to create the
  318. // directory for any reason, including if the parent directory does not
  319. // exist. Not named "CreateDirectory" because that's a macro on Windows.
  320. bool FilePath::CreateFolder() const {
  321. #if GTEST_OS_WINDOWS_MOBILE
  322.   FilePath removed_sep(this->RemoveTrailingPathSeparator());
  323.   LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
  324.   int result = CreateDirectory(unicode, NULL) ? 0 : -1;
  325.   delete [] unicode;
  326. #elif GTEST_OS_WINDOWS
  327.   int result = _mkdir(pathname_.c_str());
  328. #else
  329.   int result = mkdir(pathname_.c_str(), 0777);
  330. #endif  // GTEST_OS_WINDOWS_MOBILE
  331.  
  332.   if (result == -1) {
  333.     return this->DirectoryExists();  // An error is OK if the directory exists.
  334.   }
  335.   return true;  // No error.
  336. }
  337.  
  338. // If input name has a trailing separator character, remove it and return the
  339. // name, otherwise return the name string unmodified.
  340. // On Windows platform, uses \ as the separator, other platforms use /.
  341. FilePath FilePath::RemoveTrailingPathSeparator() const {
  342.   return IsDirectory()
  343.       ? FilePath(pathname_.substr(0, pathname_.length() - 1))
  344.       : *this;
  345. }
  346.  
  347. // Removes any redundant separators that might be in the pathname.
  348. // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
  349. // redundancies that might be in a pathname involving "." or "..".
  350. // TODO(wan@google.com): handle Windows network shares (e.g. \\server\share).
  351. void FilePath::Normalize() {
  352.   if (pathname_.c_str() == NULL) {
  353.     pathname_ = "";
  354.     return;
  355.   }
  356.   const char* src = pathname_.c_str();
  357.   char* const dest = new char[pathname_.length() + 1];
  358.   char* dest_ptr = dest;
  359.   memset(dest_ptr, 0, pathname_.length() + 1);
  360.  
  361.   while (*src != '\0') {
  362.     *dest_ptr = *src;
  363.     if (!IsPathSeparator(*src)) {
  364.       src++;
  365.     } else {
  366. #if GTEST_HAS_ALT_PATH_SEP_
  367.       if (*dest_ptr == kAlternatePathSeparator) {
  368.         *dest_ptr = kPathSeparator;
  369.       }
  370. #endif
  371.       while (IsPathSeparator(*src))
  372.         src++;
  373.     }
  374.     dest_ptr++;
  375.   }
  376.   *dest_ptr = '\0';
  377.   pathname_ = dest;
  378.   delete[] dest;
  379. }
  380.  
  381. }  // namespace internal
  382. }  // namespace testing
  383.