gtest-filepath.cc
Go to the documentation of this file.
00001 // Copyright 2008, Google Inc.
00002 // All rights reserved.
00003 //
00004 // Redistribution and use in source and binary forms, with or without
00005 // modification, are permitted provided that the following conditions are
00006 // met:
00007 //
00008 //     * Redistributions of source code must retain the above copyright
00009 // notice, this list of conditions and the following disclaimer.
00010 //     * Redistributions in binary form must reproduce the above
00011 // copyright notice, this list of conditions and the following disclaimer
00012 // in the documentation and/or other materials provided with the
00013 // distribution.
00014 //     * Neither the name of Google Inc. nor the names of its
00015 // contributors may be used to endorse or promote products derived from
00016 // this software without specific prior written permission.
00017 //
00018 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00019 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00021 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00022 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00023 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00024 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00025 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00026 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00028 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029 //
00030 // Authors: keith.ray@gmail.com (Keith Ray)
00031 
00032 #include "gtest/internal/gtest-filepath.h"
00033 #include "gtest/internal/gtest-port.h"
00034 
00035 #include <stdlib.h>
00036 
00037 #if GTEST_OS_WINDOWS_MOBILE
00038 # include <windows.h>
00039 #elif GTEST_OS_WINDOWS
00040 # include <direct.h>
00041 # include <io.h>
00042 #elif GTEST_OS_SYMBIAN || GTEST_OS_NACL
00043 // Symbian OpenC and NaCl have PATH_MAX in sys/syslimits.h
00044 # include <sys/syslimits.h>
00045 #else
00046 # include <limits.h>
00047 # include <climits>  // Some Linux distributions define PATH_MAX here.
00048 #endif  // GTEST_OS_WINDOWS_MOBILE
00049 
00050 #if GTEST_OS_WINDOWS
00051 # define GTEST_PATH_MAX_ _MAX_PATH
00052 #elif defined(PATH_MAX)
00053 # define GTEST_PATH_MAX_ PATH_MAX
00054 #elif defined(_XOPEN_PATH_MAX)
00055 # define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
00056 #else
00057 # define GTEST_PATH_MAX_ _POSIX_PATH_MAX
00058 #endif  // GTEST_OS_WINDOWS
00059 
00060 #include "gtest/internal/gtest-string.h"
00061 
00062 namespace testing {
00063 namespace internal {
00064 
00065 #if GTEST_OS_WINDOWS
00066 // On Windows, '\\' is the standard path separator, but many tools and the
00067 // Windows API also accept '/' as an alternate path separator. Unless otherwise
00068 // noted, a file path can contain either kind of path separators, or a mixture
00069 // of them.
00070 const char kPathSeparator = '\\';
00071 const char kAlternatePathSeparator = '/';
00072 const char kPathSeparatorString[] = "\\";
00073 const char kAlternatePathSeparatorString[] = "/";
00074 # if GTEST_OS_WINDOWS_MOBILE
00075 // Windows CE doesn't have a current directory. You should not use
00076 // the current directory in tests on Windows CE, but this at least
00077 // provides a reasonable fallback.
00078 const char kCurrentDirectoryString[] = "\\";
00079 // Windows CE doesn't define INVALID_FILE_ATTRIBUTES
00080 const DWORD kInvalidFileAttributes = 0xffffffff;
00081 # else
00082 const char kCurrentDirectoryString[] = ".\\";
00083 # endif  // GTEST_OS_WINDOWS_MOBILE
00084 #else
00085 const char kPathSeparator = '/';
00086 const char kPathSeparatorString[] = "/";
00087 const char kCurrentDirectoryString[] = "./";
00088 #endif  // GTEST_OS_WINDOWS
00089 
00090 // Returns whether the given character is a valid path separator.
00091 static bool IsPathSeparator(char c) {
00092 #if GTEST_HAS_ALT_PATH_SEP_
00093   return (c == kPathSeparator) || (c == kAlternatePathSeparator);
00094 #else
00095   return c == kPathSeparator;
00096 #endif
00097 }
00098 
00099 // Returns the current working directory, or "" if unsuccessful.
00100 FilePath FilePath::GetCurrentDir() {
00101 #if GTEST_OS_WINDOWS_MOBILE
00102   // Windows CE doesn't have a current directory, so we just return
00103   // something reasonable.
00104   return FilePath(kCurrentDirectoryString);
00105 #elif GTEST_OS_WINDOWS
00106   char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
00107   return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
00108 #else
00109   char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
00110   return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
00111 #endif  // GTEST_OS_WINDOWS_MOBILE
00112 }
00113 
00114 // Returns a copy of the FilePath with the case-insensitive extension removed.
00115 // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
00116 // FilePath("dir/file"). If a case-insensitive extension is not
00117 // found, returns a copy of the original FilePath.
00118 FilePath FilePath::RemoveExtension(const char* extension) const {
00119   String dot_extension(String::Format(".%s", extension));
00120   if (pathname_.EndsWithCaseInsensitive(dot_extension.c_str())) {
00121     return FilePath(String(pathname_.c_str(), pathname_.length() - 4));
00122   }
00123   return *this;
00124 }
00125 
00126 // Returns a pointer to the last occurence of a valid path separator in
00127 // the FilePath. On Windows, for example, both '/' and '\' are valid path
00128 // separators. Returns NULL if no path separator was found.
00129 const char* FilePath::FindLastPathSeparator() const {
00130   const char* const last_sep = strrchr(c_str(), kPathSeparator);
00131 #if GTEST_HAS_ALT_PATH_SEP_
00132   const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
00133   // Comparing two pointers of which only one is NULL is undefined.
00134   if (last_alt_sep != NULL &&
00135       (last_sep == NULL || last_alt_sep > last_sep)) {
00136     return last_alt_sep;
00137   }
00138 #endif
00139   return last_sep;
00140 }
00141 
00142 // Returns a copy of the FilePath with the directory part removed.
00143 // Example: FilePath("path/to/file").RemoveDirectoryName() returns
00144 // FilePath("file"). If there is no directory part ("just_a_file"), it returns
00145 // the FilePath unmodified. If there is no file part ("just_a_dir/") it
00146 // returns an empty FilePath ("").
00147 // On Windows platform, '\' is the path separator, otherwise it is '/'.
00148 FilePath FilePath::RemoveDirectoryName() const {
00149   const char* const last_sep = FindLastPathSeparator();
00150   return last_sep ? FilePath(String(last_sep + 1)) : *this;
00151 }
00152 
00153 // RemoveFileName returns the directory path with the filename removed.
00154 // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
00155 // If the FilePath is "a_file" or "/a_file", RemoveFileName returns
00156 // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
00157 // not have a file, like "just/a/dir/", it returns the FilePath unmodified.
00158 // On Windows platform, '\' is the path separator, otherwise it is '/'.
00159 FilePath FilePath::RemoveFileName() const {
00160   const char* const last_sep = FindLastPathSeparator();
00161   String dir;
00162   if (last_sep) {
00163     dir = String(c_str(), last_sep + 1 - c_str());
00164   } else {
00165     dir = kCurrentDirectoryString;
00166   }
00167   return FilePath(dir);
00168 }
00169 
00170 // Helper functions for naming files in a directory for xml output.
00171 
00172 // Given directory = "dir", base_name = "test", number = 0,
00173 // extension = "xml", returns "dir/test.xml". If number is greater
00174 // than zero (e.g., 12), returns "dir/test_12.xml".
00175 // On Windows platform, uses \ as the separator rather than /.
00176 FilePath FilePath::MakeFileName(const FilePath& directory,
00177                                 const FilePath& base_name,
00178                                 int number,
00179                                 const char* extension) {
00180   String file;
00181   if (number == 0) {
00182     file = String::Format("%s.%s", base_name.c_str(), extension);
00183   } else {
00184     file = String::Format("%s_%d.%s", base_name.c_str(), number, extension);
00185   }
00186   return ConcatPaths(directory, FilePath(file));
00187 }
00188 
00189 // Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
00190 // On Windows, uses \ as the separator rather than /.
00191 FilePath FilePath::ConcatPaths(const FilePath& directory,
00192                                const FilePath& relative_path) {
00193   if (directory.IsEmpty())
00194     return relative_path;
00195   const FilePath dir(directory.RemoveTrailingPathSeparator());
00196   return FilePath(String::Format("%s%c%s", dir.c_str(), kPathSeparator,
00197                                  relative_path.c_str()));
00198 }
00199 
00200 // Returns true if pathname describes something findable in the file-system,
00201 // either a file, directory, or whatever.
00202 bool FilePath::FileOrDirectoryExists() const {
00203 #if GTEST_OS_WINDOWS_MOBILE
00204   LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
00205   const DWORD attributes = GetFileAttributes(unicode);
00206   delete [] unicode;
00207   return attributes != kInvalidFileAttributes;
00208 #else
00209   posix::StatStruct file_stat;
00210   return posix::Stat(pathname_.c_str(), &file_stat) == 0;
00211 #endif  // GTEST_OS_WINDOWS_MOBILE
00212 }
00213 
00214 // Returns true if pathname describes a directory in the file-system
00215 // that exists.
00216 bool FilePath::DirectoryExists() const {
00217   bool result = false;
00218 #if GTEST_OS_WINDOWS
00219   // Don't strip off trailing separator if path is a root directory on
00220   // Windows (like "C:\\").
00221   const FilePath& path(IsRootDirectory() ? *this :
00222                                            RemoveTrailingPathSeparator());
00223 #else
00224   const FilePath& path(*this);
00225 #endif
00226 
00227 #if GTEST_OS_WINDOWS_MOBILE
00228   LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
00229   const DWORD attributes = GetFileAttributes(unicode);
00230   delete [] unicode;
00231   if ((attributes != kInvalidFileAttributes) &&
00232       (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
00233     result = true;
00234   }
00235 #else
00236   posix::StatStruct file_stat;
00237   result = posix::Stat(path.c_str(), &file_stat) == 0 &&
00238       posix::IsDir(file_stat);
00239 #endif  // GTEST_OS_WINDOWS_MOBILE
00240 
00241   return result;
00242 }
00243 
00244 // Returns true if pathname describes a root directory. (Windows has one
00245 // root directory per disk drive.)
00246 bool FilePath::IsRootDirectory() const {
00247 #if GTEST_OS_WINDOWS
00248   // TODO(wan@google.com): on Windows a network share like
00249   // \\server\share can be a root directory, although it cannot be the
00250   // current directory.  Handle this properly.
00251   return pathname_.length() == 3 && IsAbsolutePath();
00252 #else
00253   return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
00254 #endif
00255 }
00256 
00257 // Returns true if pathname describes an absolute path.
00258 bool FilePath::IsAbsolutePath() const {
00259   const char* const name = pathname_.c_str();
00260 #if GTEST_OS_WINDOWS
00261   return pathname_.length() >= 3 &&
00262      ((name[0] >= 'a' && name[0] <= 'z') ||
00263       (name[0] >= 'A' && name[0] <= 'Z')) &&
00264      name[1] == ':' &&
00265      IsPathSeparator(name[2]);
00266 #else
00267   return IsPathSeparator(name[0]);
00268 #endif
00269 }
00270 
00271 // Returns a pathname for a file that does not currently exist. The pathname
00272 // will be directory/base_name.extension or
00273 // directory/base_name_<number>.extension if directory/base_name.extension
00274 // already exists. The number will be incremented until a pathname is found
00275 // that does not already exist.
00276 // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
00277 // There could be a race condition if two or more processes are calling this
00278 // function at the same time -- they could both pick the same filename.
00279 FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
00280                                           const FilePath& base_name,
00281                                           const char* extension) {
00282   FilePath full_pathname;
00283   int number = 0;
00284   do {
00285     full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
00286   } while (full_pathname.FileOrDirectoryExists());
00287   return full_pathname;
00288 }
00289 
00290 // Returns true if FilePath ends with a path separator, which indicates that
00291 // it is intended to represent a directory. Returns false otherwise.
00292 // This does NOT check that a directory (or file) actually exists.
00293 bool FilePath::IsDirectory() const {
00294   return !pathname_.empty() &&
00295          IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
00296 }
00297 
00298 // Create directories so that path exists. Returns true if successful or if
00299 // the directories already exist; returns false if unable to create directories
00300 // for any reason.
00301 bool FilePath::CreateDirectoriesRecursively() const {
00302   if (!this->IsDirectory()) {
00303     return false;
00304   }
00305 
00306   if (pathname_.length() == 0 || this->DirectoryExists()) {
00307     return true;
00308   }
00309 
00310   const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
00311   return parent.CreateDirectoriesRecursively() && this->CreateFolder();
00312 }
00313 
00314 // Create the directory so that path exists. Returns true if successful or
00315 // if the directory already exists; returns false if unable to create the
00316 // directory for any reason, including if the parent directory does not
00317 // exist. Not named "CreateDirectory" because that's a macro on Windows.
00318 bool FilePath::CreateFolder() const {
00319 #if GTEST_OS_WINDOWS_MOBILE
00320   FilePath removed_sep(this->RemoveTrailingPathSeparator());
00321   LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
00322   int result = CreateDirectory(unicode, NULL) ? 0 : -1;
00323   delete [] unicode;
00324 #elif GTEST_OS_WINDOWS
00325   int result = _mkdir(pathname_.c_str());
00326 #else
00327   int result = mkdir(pathname_.c_str(), 0777);
00328 #endif  // GTEST_OS_WINDOWS_MOBILE
00329 
00330   if (result == -1) {
00331     return this->DirectoryExists();  // An error is OK if the directory exists.
00332   }
00333   return true;  // No error.
00334 }
00335 
00336 // If input name has a trailing separator character, remove it and return the
00337 // name, otherwise return the name string unmodified.
00338 // On Windows platform, uses \ as the separator, other platforms use /.
00339 FilePath FilePath::RemoveTrailingPathSeparator() const {
00340   return IsDirectory()
00341       ? FilePath(String(pathname_.c_str(), pathname_.length() - 1))
00342       : *this;
00343 }
00344 
00345 // Removes any redundant separators that might be in the pathname.
00346 // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
00347 // redundancies that might be in a pathname involving "." or "..".
00348 // TODO(wan@google.com): handle Windows network shares (e.g. \\server\share).
00349 void FilePath::Normalize() {
00350   if (pathname_.c_str() == NULL) {
00351     pathname_ = "";
00352     return;
00353   }
00354   const char* src = pathname_.c_str();
00355   char* const dest = new char[pathname_.length() + 1];
00356   char* dest_ptr = dest;
00357   memset(dest_ptr, 0, pathname_.length() + 1);
00358 
00359   while (*src != '\0') {
00360     *dest_ptr = *src;
00361     if (!IsPathSeparator(*src)) {
00362       src++;
00363     } else {
00364 #if GTEST_HAS_ALT_PATH_SEP_
00365       if (*dest_ptr == kAlternatePathSeparator) {
00366         *dest_ptr = kPathSeparator;
00367       }
00368 #endif
00369       while (IsPathSeparator(*src))
00370         src++;
00371     }
00372     dest_ptr++;
00373   }
00374   *dest_ptr = '\0';
00375   pathname_ = dest;
00376   delete[] dest;
00377 }
00378 
00379 }  // namespace internal
00380 }  // namespace testing


pcl
Author(s): Open Perception
autogenerated on Wed Aug 26 2015 15:24:39