gtest-filepath.cc 14 KB

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