1/*------------------------------------------------------------------------- 2 * drawElements C++ Base Library 3 * ----------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Filesystem path class. 22 *//*--------------------------------------------------------------------*/ 23 24#include "deFilePath.hpp" 25 26#include <vector> 27#include <stdexcept> 28 29#include <sys/stat.h> 30#include <sys/types.h> 31 32#if (DE_OS == DE_OS_WIN32) 33# define VC_EXTRALEAN 34# define WIN32_LEAN_AND_MEAN 35# define NOMINMAX 36# include <windows.h> 37#endif 38 39using std::string; 40 41namespace de 42{ 43 44#if (DE_OS == DE_OS_WIN32) 45 const std::string FilePath::separator = "\\"; 46#else 47 const std::string FilePath::separator = "/"; 48#endif 49 50FilePath::FilePath (const std::vector<std::string>& components) 51{ 52 for (size_t ndx = 0; ndx < components.size(); ndx++) 53 { 54 if (!m_path.empty() && !isSeparator(m_path[m_path.size()-1])) 55 m_path += separator; 56 m_path += components[ndx]; 57 } 58} 59 60void FilePath::split (std::vector<std::string>& components) const 61{ 62 components.clear(); 63 64 int curCompStart = 0; 65 int pos; 66 67 if (isWinNetPath()) 68 components.push_back(separator + separator); 69 else if (isRootPath() && !beginsWithDrive()) 70 components.push_back(separator); 71 72 for (pos = 0; pos < (int)m_path.length(); pos++) 73 { 74 const char c = m_path[pos]; 75 76 if (isSeparator(c)) 77 { 78 if (pos - curCompStart > 0) 79 components.push_back(m_path.substr(curCompStart, pos - curCompStart)); 80 81 curCompStart = pos+1; 82 } 83 } 84 85 if (pos - curCompStart > 0) 86 components.push_back(m_path.substr(curCompStart, pos - curCompStart)); 87} 88 89FilePath FilePath::join (const std::vector<std::string>& components) 90{ 91 return FilePath(components); 92} 93 94FilePath& FilePath::normalize (void) 95{ 96 std::vector<std::string> components; 97 std::vector<std::string> reverseNormalizedComponents; 98 99 split(components); 100 101 m_path = ""; 102 103 int numUp = 0; 104 105 // Do in reverse order and eliminate any . or .. components 106 for (int ndx = (int)components.size()-1; ndx >= 0; ndx--) 107 { 108 const std::string& comp = components[ndx]; 109 if (comp == "..") 110 numUp += 1; 111 else if (comp == ".") 112 continue; 113 else if (numUp > 0) 114 numUp -= 1; // Skip part 115 else 116 reverseNormalizedComponents.push_back(comp); 117 } 118 119 if (isAbsolutePath() && numUp > 0) 120 throw std::runtime_error("Cannot normalize path: invalid path"); 121 122 // Prepend necessary ".." components 123 while (numUp--) 124 reverseNormalizedComponents.push_back(".."); 125 126 if (reverseNormalizedComponents.empty() && components.back() == ".") 127 reverseNormalizedComponents.push_back("."); // Composed of "." components only 128 129 *this = join(std::vector<std::string>(reverseNormalizedComponents.rbegin(), reverseNormalizedComponents.rend())); 130 131 return *this; 132} 133 134FilePath FilePath::normalize (const FilePath& path) 135{ 136 return FilePath(path).normalize(); 137} 138 139std::string FilePath::getBaseName (void) const 140{ 141 std::vector<std::string> components; 142 split(components); 143 return !components.empty() ? components[components.size()-1] : std::string(""); 144} 145 146std::string FilePath::getDirName (void) const 147{ 148 std::vector<std::string> components; 149 split(components); 150 if (components.size() > 1) 151 { 152 components.pop_back(); 153 return FilePath(components).getPath(); 154 } 155 else if (isAbsolutePath()) 156 return separator; 157 else 158 return std::string("."); 159} 160 161std::string FilePath::getFileExtension (void) const 162{ 163 std::string baseName = getBaseName(); 164 size_t dotPos = baseName.find_last_of('.'); 165 if (dotPos == std::string::npos) 166 return std::string(""); 167 else 168 return baseName.substr(dotPos+1); 169} 170 171bool FilePath::exists (void) const 172{ 173 FilePath normPath = FilePath::normalize(*this); 174 struct stat st; 175 int result = stat(normPath.getPath(), &st); 176 return result == 0; 177} 178 179FilePath::Type FilePath::getType (void) const 180{ 181 FilePath normPath = FilePath::normalize(*this); 182 struct stat st; 183 int result = stat(normPath.getPath(), &st); 184 185 if (result != 0) 186 return TYPE_UNKNOWN; 187 188 int type = st.st_mode & S_IFMT; 189 if (type == S_IFREG) 190 return TYPE_FILE; 191 else if (type == S_IFDIR) 192 return TYPE_DIRECTORY; 193 else 194 return TYPE_UNKNOWN; 195} 196 197bool FilePath::beginsWithDrive (void) const 198{ 199 for (int ndx = 0; ndx < (int)m_path.length(); ndx++) 200 { 201 if (m_path[ndx] == ':' && ndx+1 < (int)m_path.length() && isSeparator(m_path[ndx+1])) 202 return true; // First part is drive letter. 203 if (isSeparator(m_path[ndx])) 204 return false; 205 } 206 return false; 207} 208 209bool FilePath::isAbsolutePath (void) const 210{ 211 return isRootPath() || isWinNetPath() || beginsWithDrive(); 212} 213 214void FilePath_selfTest (void) 215{ 216 DE_TEST_ASSERT(!FilePath(".").isAbsolutePath()); 217 DE_TEST_ASSERT(!FilePath("..\\foo").isAbsolutePath()); 218 DE_TEST_ASSERT(!FilePath("foo").isAbsolutePath()); 219 DE_TEST_ASSERT(FilePath("\\foo/bar").isAbsolutePath()); 220 DE_TEST_ASSERT(FilePath("/foo").isAbsolutePath()); 221 DE_TEST_ASSERT(FilePath("\\").isAbsolutePath()); 222 DE_TEST_ASSERT(FilePath("\\\\net\\loc").isAbsolutePath()); 223 DE_TEST_ASSERT(FilePath("C:\\file.txt").isAbsolutePath()); 224 DE_TEST_ASSERT(FilePath("c:/file.txt").isAbsolutePath()); 225 226 DE_TEST_ASSERT(string(".") == FilePath(".//.").normalize().getPath()); 227 DE_TEST_ASSERT(string(".") == FilePath(".").normalize().getPath()); 228 DE_TEST_ASSERT((string("..") + FilePath::separator + "test") == FilePath("foo/../bar/../../test").normalize().getPath()); 229 DE_TEST_ASSERT((FilePath::separator + "foo" + FilePath::separator + "foo.txt") == FilePath("/foo\\bar/..\\dir\\..\\foo.txt").normalize().getPath()); 230 DE_TEST_ASSERT((string("c:") + FilePath::separator + "foo" + FilePath::separator + "foo.txt") == FilePath("c:/foo\\bar/..\\dir\\..\\foo.txt").normalize().getPath()); 231 DE_TEST_ASSERT((FilePath::separator + FilePath::separator + "foo" + FilePath::separator + "foo.txt") == FilePath("\\\\foo\\bar/..\\dir\\..\\foo.txt").normalize().getPath()); 232 233 DE_TEST_ASSERT(FilePath("foo/bar" ).getBaseName() == "bar"); 234 DE_TEST_ASSERT(FilePath("foo/bar/" ).getBaseName() == "bar"); 235 DE_TEST_ASSERT(FilePath("foo\\bar" ).getBaseName() == "bar"); 236 DE_TEST_ASSERT(FilePath("foo\\bar\\" ).getBaseName() == "bar"); 237 DE_TEST_ASSERT(FilePath("foo/bar" ).getDirName() == "foo"); 238 DE_TEST_ASSERT(FilePath("foo/bar/" ).getDirName() == "foo"); 239 DE_TEST_ASSERT(FilePath("foo\\bar" ).getDirName() == "foo"); 240 DE_TEST_ASSERT(FilePath("foo\\bar\\" ).getDirName() == "foo"); 241 DE_TEST_ASSERT(FilePath("/foo/bar/baz" ).getDirName() == FilePath::separator + "foo" + FilePath::separator + "bar"); 242} 243 244static void createDirectoryImpl (const char* path) 245{ 246#if (DE_OS == DE_OS_WIN32) 247 if (!CreateDirectory(path, DE_NULL)) 248 throw std::runtime_error("Failed to create directory"); 249#elif (DE_OS == DE_OS_UNIX) || (DE_OS == DE_OS_OSX) || (DE_OS == DE_OS_IOS) || (DE_OS == DE_OS_ANDROID) || (DE_OS == DE_OS_SYMBIAN) || (DE_OS == DE_OS_QNX) || (DE_OS == DE_OS_FUCHSIA) 250 if (mkdir(path, 0777) != 0) 251 throw std::runtime_error("Failed to create directory"); 252#else 253# error Implement createDirectoryImpl() for your platform. 254#endif 255} 256 257void createDirectory (const char* path) 258{ 259 FilePath dirPath = FilePath::normalize(path); 260 FilePath parentPath (dirPath.getDirName()); 261 262 if (dirPath.exists()) 263 throw std::runtime_error("Destination exists already"); 264 else if (!parentPath.exists()) 265 throw std::runtime_error("Parent directory doesn't exist"); 266 else if (parentPath.getType() != FilePath::TYPE_DIRECTORY) 267 throw std::runtime_error("Parent is not directory"); 268 269 createDirectoryImpl(path); 270} 271 272void createDirectoryAndParents (const char* path) 273{ 274 std::vector<std::string> createPaths; 275 FilePath curPath (path); 276 277 if (curPath.exists()) 278 throw std::runtime_error("Destination exists already"); 279 280 while (!curPath.exists()) 281 { 282 createPaths.push_back(curPath.getPath()); 283 284 std::string parent = curPath.getDirName(); 285 DE_CHECK_RUNTIME_ERR(parent != curPath.getPath()); 286 curPath = FilePath(parent); 287 } 288 289 // Create in reverse order. 290 for (std::vector<std::string>::const_reverse_iterator parentIter = createPaths.rbegin(); parentIter != createPaths.rend(); parentIter++) 291 createDirectory(parentIter->c_str()); 292} 293 294} // de 295