1 /*-------------------------------------------------------------------------
2 * drawElements C++ Base Library
3 * -----------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Filesystem path class.
22 *//*--------------------------------------------------------------------*/
24 #include "deFilePath.hpp"
30 #include <sys/types.h>
32 #if (DE_OS == DE_OS_WIN32)
34 # define WIN32_LEAN_AND_MEAN
44 #if (DE_OS == DE_OS_WIN32)
45 const std::string FilePath::separator = "\\";
47 const std::string FilePath::separator = "/";
50 FilePath::FilePath (const std::vector<std::string>& components)
52 for (size_t ndx = 0; ndx < components.size(); ndx++)
54 if (!m_path.empty() && !isSeparator(m_path[m_path.size()-1]))
56 m_path += components[ndx];
60 void FilePath::split (std::vector<std::string>& components) const
68 components.push_back(separator + separator);
69 else if (isRootPath() && !beginsWithDrive())
70 components.push_back(separator);
72 for (pos = 0; pos < (int)m_path.length(); pos++)
74 const char c = m_path[pos];
78 if (pos - curCompStart > 0)
79 components.push_back(m_path.substr(curCompStart, pos - curCompStart));
85 if (pos - curCompStart > 0)
86 components.push_back(m_path.substr(curCompStart, pos - curCompStart));
89 FilePath FilePath::join (const std::vector<std::string>& components)
91 return FilePath(components);
94 FilePath& FilePath::normalize (void)
96 std::vector<std::string> components;
97 std::vector<std::string> reverseNormalizedComponents;
105 // Do in reverse order and eliminate any . or .. components
106 for (int ndx = (int)components.size()-1; ndx >= 0; ndx--)
108 const std::string& comp = components[ndx];
111 else if (comp == ".")
114 numUp -= 1; // Skip part
116 reverseNormalizedComponents.push_back(comp);
119 if (isAbsolutePath() && numUp > 0)
120 throw std::runtime_error("Cannot normalize path: invalid path");
122 // Prepend necessary ".." components
124 reverseNormalizedComponents.push_back("..");
126 if (reverseNormalizedComponents.empty() && components.back() == ".")
127 reverseNormalizedComponents.push_back("."); // Composed of "." components only
129 *this = join(std::vector<std::string>(reverseNormalizedComponents.rbegin(), reverseNormalizedComponents.rend()));
134 FilePath FilePath::normalize (const FilePath& path)
136 return FilePath(path).normalize();
139 std::string FilePath::getBaseName (void) const
141 std::vector<std::string> components;
143 return !components.empty() ? components[components.size()-1] : std::string("");
146 std::string FilePath::getDirName (void) const
148 std::vector<std::string> components;
150 if (components.size() > 1)
152 components.pop_back();
153 return FilePath(components).getPath();
155 else if (isAbsolutePath())
158 return std::string(".");
161 std::string FilePath::getFileExtension (void) const
163 std::string baseName = getBaseName();
164 size_t dotPos = baseName.find_last_of('.');
165 if (dotPos == std::string::npos)
166 return std::string("");
168 return baseName.substr(dotPos+1);
171 bool FilePath::exists (void) const
173 FilePath normPath = FilePath::normalize(*this);
175 int result = stat(normPath.getPath(), &st);
179 FilePath::Type FilePath::getType (void) const
181 FilePath normPath = FilePath::normalize(*this);
183 int result = stat(normPath.getPath(), &st);
188 int type = st.st_mode & S_IFMT;
191 else if (type == S_IFDIR)
192 return TYPE_DIRECTORY;
197 bool FilePath::beginsWithDrive (void) const
199 for (int ndx = 0; ndx < (int)m_path.length(); ndx++)
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]))
209 bool FilePath::isAbsolutePath (void) const
211 return isRootPath() || isWinNetPath() || beginsWithDrive();
214 void FilePath_selfTest (void)
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());
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());
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");
244 static void createDirectoryImpl (const char* path)
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)
250 if (mkdir(path, 0777) != 0)
251 throw std::runtime_error("Failed to create directory");
253 # error Implement createDirectoryImpl() for your platform.
257 void createDirectory (const char* path)
259 FilePath dirPath = FilePath::normalize(path);
260 FilePath parentPath (dirPath.getDirName());
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");
269 createDirectoryImpl(path);
272 void createDirectoryAndParents (const char* path)
274 std::vector<std::string> createPaths;
275 FilePath curPath (path);
277 if (curPath.exists())
278 throw std::runtime_error("Destination exists already");
280 while (!curPath.exists())
282 createPaths.push_back(curPath.getPath());
284 std::string parent = curPath.getDirName();
285 DE_CHECK_RUNTIME_ERR(parent != curPath.getPath());
286 curPath = FilePath(parent);
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());