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 39 using std::string; 40 41 namespace 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 50 FilePath::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 60 void 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 89 FilePath FilePath::join (const std::vector<std::string>& components) 90 { 91 return FilePath(components); 92 } 93 94 FilePath& 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 134 FilePath FilePath::normalize (const FilePath& path) 135 { 136 return FilePath(path).normalize(); 137 } 138 139 std::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 146 std::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 161 std::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 171 bool 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 179 FilePath::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 197 bool 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 209 bool FilePath::isAbsolutePath (void) const 210 { 211 return isRootPath() || isWinNetPath() || beginsWithDrive(); 212 } 213 214 void 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 244 static 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) 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 257 void 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 272 void 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