gtest-filepath.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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/internal/gtest-port.h"
  32. #include "gtest/gtest-message.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(pathname_.substr(
  115. 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,
  167. int number,
  168. const char* extension) {
  169. std::string file;
  170. if (number == 0) {
  171. file = base_name.string() + "." + extension;
  172. } else {
  173. file = base_name.string() + "_" + StreamableToString(number)
  174. + "." + extension;
  175. }
  176. return ConcatPaths(directory, FilePath(file));
  177. }
  178. // Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
  179. // On Windows, uses \ as the separator rather than /.
  180. FilePath FilePath::ConcatPaths(const FilePath& directory,
  181. const FilePath& relative_path) {
  182. if (directory.IsEmpty())
  183. return relative_path;
  184. const FilePath dir(directory.RemoveTrailingPathSeparator());
  185. return FilePath(dir.string() + kPathSeparator + relative_path.string());
  186. }
  187. // Returns true if pathname describes something findable in the file-system,
  188. // either a file, directory, or whatever.
  189. bool FilePath::FileOrDirectoryExists() const {
  190. #if GTEST_OS_WINDOWS_MOBILE
  191. LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
  192. const DWORD attributes = GetFileAttributes(unicode);
  193. delete [] unicode;
  194. return attributes != kInvalidFileAttributes;
  195. #else
  196. posix::StatStruct file_stat{};
  197. return posix::Stat(pathname_.c_str(), &file_stat) == 0;
  198. #endif // GTEST_OS_WINDOWS_MOBILE
  199. }
  200. // Returns true if pathname describes a directory in the file-system
  201. // that exists.
  202. bool FilePath::DirectoryExists() const {
  203. bool result = false;
  204. #if GTEST_OS_WINDOWS
  205. // Don't strip off trailing separator if path is a root directory on
  206. // Windows (like "C:\\").
  207. const FilePath& path(IsRootDirectory() ? *this :
  208. RemoveTrailingPathSeparator());
  209. #else
  210. const FilePath& path(*this);
  211. #endif
  212. #if GTEST_OS_WINDOWS_MOBILE
  213. LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
  214. const DWORD attributes = GetFileAttributes(unicode);
  215. delete [] unicode;
  216. if ((attributes != kInvalidFileAttributes) &&
  217. (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
  218. result = true;
  219. }
  220. #else
  221. posix::StatStruct file_stat{};
  222. result = posix::Stat(path.c_str(), &file_stat) == 0 &&
  223. posix::IsDir(file_stat);
  224. #endif // GTEST_OS_WINDOWS_MOBILE
  225. return result;
  226. }
  227. // Returns true if pathname describes a root directory. (Windows has one
  228. // root directory per disk drive.)
  229. bool FilePath::IsRootDirectory() const {
  230. #if GTEST_OS_WINDOWS
  231. return pathname_.length() == 3 && IsAbsolutePath();
  232. #else
  233. return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
  234. #endif
  235. }
  236. // Returns true if pathname describes an absolute path.
  237. bool FilePath::IsAbsolutePath() const {
  238. const char* const name = pathname_.c_str();
  239. #if GTEST_OS_WINDOWS
  240. return pathname_.length() >= 3 &&
  241. ((name[0] >= 'a' && name[0] <= 'z') ||
  242. (name[0] >= 'A' && name[0] <= 'Z')) &&
  243. name[1] == ':' &&
  244. IsPathSeparator(name[2]);
  245. #else
  246. return IsPathSeparator(name[0]);
  247. #endif
  248. }
  249. // Returns a pathname for a file that does not currently exist. The pathname
  250. // will be directory/base_name.extension or
  251. // directory/base_name_<number>.extension if directory/base_name.extension
  252. // already exists. The number will be incremented until a pathname is found
  253. // that does not already exist.
  254. // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
  255. // There could be a race condition if two or more processes are calling this
  256. // function at the same time -- they could both pick the same filename.
  257. FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
  258. const FilePath& base_name,
  259. const char* extension) {
  260. FilePath full_pathname;
  261. int number = 0;
  262. do {
  263. full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
  264. } while (full_pathname.FileOrDirectoryExists());
  265. return full_pathname;
  266. }
  267. // Returns true if FilePath ends with a path separator, which indicates that
  268. // it is intended to represent a directory. Returns false otherwise.
  269. // This does NOT check that a directory (or file) actually exists.
  270. bool FilePath::IsDirectory() const {
  271. return !pathname_.empty() &&
  272. IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
  273. }
  274. // Create directories so that path exists. Returns true if successful or if
  275. // the directories already exist; returns false if unable to create directories
  276. // for any reason.
  277. bool FilePath::CreateDirectoriesRecursively() const {
  278. if (!this->IsDirectory()) {
  279. return false;
  280. }
  281. if (pathname_.length() == 0 || this->DirectoryExists()) {
  282. return true;
  283. }
  284. const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
  285. return parent.CreateDirectoriesRecursively() && this->CreateFolder();
  286. }
  287. // Create the directory so that path exists. Returns true if successful or
  288. // if the directory already exists; returns false if unable to create the
  289. // directory for any reason, including if the parent directory does not
  290. // exist. Not named "CreateDirectory" because that's a macro on Windows.
  291. bool FilePath::CreateFolder() const {
  292. #if GTEST_OS_WINDOWS_MOBILE
  293. FilePath removed_sep(this->RemoveTrailingPathSeparator());
  294. LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
  295. int result = CreateDirectory(unicode, nullptr) ? 0 : -1;
  296. delete [] unicode;
  297. #elif GTEST_OS_WINDOWS
  298. int result = _mkdir(pathname_.c_str());
  299. #elif GTEST_OS_ESP8266 || GTEST_OS_XTENSA
  300. // do nothing
  301. int result = 0;
  302. #else
  303. int result = mkdir(pathname_.c_str(), 0777);
  304. #endif // GTEST_OS_WINDOWS_MOBILE
  305. if (result == -1) {
  306. return this->DirectoryExists(); // An error is OK if the directory exists.
  307. }
  308. return true; // No error.
  309. }
  310. // If input name has a trailing separator character, remove it and return the
  311. // name, otherwise return the name string unmodified.
  312. // On Windows platform, uses \ as the separator, other platforms use /.
  313. FilePath FilePath::RemoveTrailingPathSeparator() const {
  314. return IsDirectory()
  315. ? FilePath(pathname_.substr(0, pathname_.length() - 1))
  316. : *this;
  317. }
  318. // Removes any redundant separators that might be in the pathname.
  319. // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
  320. // redundancies that might be in a pathname involving "." or "..".
  321. void FilePath::Normalize() {
  322. auto out = pathname_.begin();
  323. for (const char character : pathname_) {
  324. if (!IsPathSeparator(character)) {
  325. *(out++) = character;
  326. } else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) {
  327. *(out++) = kPathSeparator;
  328. } else {
  329. continue;
  330. }
  331. }
  332. pathname_.erase(out, pathname_.end());
  333. }
  334. } // namespace internal
  335. } // namespace testing