You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
295 lines
8.4 KiB
295 lines
8.4 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements C++ Base Library
|
|
* -----------------------------
|
|
*
|
|
* Copyright 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief Filesystem path class.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "deFilePath.hpp"
|
|
|
|
#include <vector>
|
|
#include <stdexcept>
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#if (DE_OS == DE_OS_WIN32)
|
|
# define VC_EXTRALEAN
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# define NOMINMAX
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
using std::string;
|
|
|
|
namespace de
|
|
{
|
|
|
|
#if (DE_OS == DE_OS_WIN32)
|
|
const std::string FilePath::separator = "\\";
|
|
#else
|
|
const std::string FilePath::separator = "/";
|
|
#endif
|
|
|
|
FilePath::FilePath (const std::vector<std::string>& components)
|
|
{
|
|
for (size_t ndx = 0; ndx < components.size(); ndx++)
|
|
{
|
|
if (!m_path.empty() && !isSeparator(m_path[m_path.size()-1]))
|
|
m_path += separator;
|
|
m_path += components[ndx];
|
|
}
|
|
}
|
|
|
|
void FilePath::split (std::vector<std::string>& components) const
|
|
{
|
|
components.clear();
|
|
|
|
int curCompStart = 0;
|
|
int pos;
|
|
|
|
if (isWinNetPath())
|
|
components.push_back(separator + separator);
|
|
else if (isRootPath() && !beginsWithDrive())
|
|
components.push_back(separator);
|
|
|
|
for (pos = 0; pos < (int)m_path.length(); pos++)
|
|
{
|
|
const char c = m_path[pos];
|
|
|
|
if (isSeparator(c))
|
|
{
|
|
if (pos - curCompStart > 0)
|
|
components.push_back(m_path.substr(curCompStart, pos - curCompStart));
|
|
|
|
curCompStart = pos+1;
|
|
}
|
|
}
|
|
|
|
if (pos - curCompStart > 0)
|
|
components.push_back(m_path.substr(curCompStart, pos - curCompStart));
|
|
}
|
|
|
|
FilePath FilePath::join (const std::vector<std::string>& components)
|
|
{
|
|
return FilePath(components);
|
|
}
|
|
|
|
FilePath& FilePath::normalize (void)
|
|
{
|
|
std::vector<std::string> components;
|
|
std::vector<std::string> reverseNormalizedComponents;
|
|
|
|
split(components);
|
|
|
|
m_path = "";
|
|
|
|
int numUp = 0;
|
|
|
|
// Do in reverse order and eliminate any . or .. components
|
|
for (int ndx = (int)components.size()-1; ndx >= 0; ndx--)
|
|
{
|
|
const std::string& comp = components[ndx];
|
|
if (comp == "..")
|
|
numUp += 1;
|
|
else if (comp == ".")
|
|
continue;
|
|
else if (numUp > 0)
|
|
numUp -= 1; // Skip part
|
|
else
|
|
reverseNormalizedComponents.push_back(comp);
|
|
}
|
|
|
|
if (isAbsolutePath() && numUp > 0)
|
|
throw std::runtime_error("Cannot normalize path: invalid path");
|
|
|
|
// Prepend necessary ".." components
|
|
while (numUp--)
|
|
reverseNormalizedComponents.push_back("..");
|
|
|
|
if (reverseNormalizedComponents.empty() && components.back() == ".")
|
|
reverseNormalizedComponents.push_back("."); // Composed of "." components only
|
|
|
|
*this = join(std::vector<std::string>(reverseNormalizedComponents.rbegin(), reverseNormalizedComponents.rend()));
|
|
|
|
return *this;
|
|
}
|
|
|
|
FilePath FilePath::normalize (const FilePath& path)
|
|
{
|
|
return FilePath(path).normalize();
|
|
}
|
|
|
|
std::string FilePath::getBaseName (void) const
|
|
{
|
|
std::vector<std::string> components;
|
|
split(components);
|
|
return !components.empty() ? components[components.size()-1] : std::string("");
|
|
}
|
|
|
|
std::string FilePath::getDirName (void) const
|
|
{
|
|
std::vector<std::string> components;
|
|
split(components);
|
|
if (components.size() > 1)
|
|
{
|
|
components.pop_back();
|
|
return FilePath(components).getPath();
|
|
}
|
|
else if (isAbsolutePath())
|
|
return separator;
|
|
else
|
|
return std::string(".");
|
|
}
|
|
|
|
std::string FilePath::getFileExtension (void) const
|
|
{
|
|
std::string baseName = getBaseName();
|
|
size_t dotPos = baseName.find_last_of('.');
|
|
if (dotPos == std::string::npos)
|
|
return std::string("");
|
|
else
|
|
return baseName.substr(dotPos+1);
|
|
}
|
|
|
|
bool FilePath::exists (void) const
|
|
{
|
|
FilePath normPath = FilePath::normalize(*this);
|
|
struct stat st;
|
|
int result = stat(normPath.getPath(), &st);
|
|
return result == 0;
|
|
}
|
|
|
|
FilePath::Type FilePath::getType (void) const
|
|
{
|
|
FilePath normPath = FilePath::normalize(*this);
|
|
struct stat st;
|
|
int result = stat(normPath.getPath(), &st);
|
|
|
|
if (result != 0)
|
|
return TYPE_UNKNOWN;
|
|
|
|
int type = st.st_mode & S_IFMT;
|
|
if (type == S_IFREG)
|
|
return TYPE_FILE;
|
|
else if (type == S_IFDIR)
|
|
return TYPE_DIRECTORY;
|
|
else
|
|
return TYPE_UNKNOWN;
|
|
}
|
|
|
|
bool FilePath::beginsWithDrive (void) const
|
|
{
|
|
for (int ndx = 0; ndx < (int)m_path.length(); ndx++)
|
|
{
|
|
if (m_path[ndx] == ':' && ndx+1 < (int)m_path.length() && isSeparator(m_path[ndx+1]))
|
|
return true; // First part is drive letter.
|
|
if (isSeparator(m_path[ndx]))
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FilePath::isAbsolutePath (void) const
|
|
{
|
|
return isRootPath() || isWinNetPath() || beginsWithDrive();
|
|
}
|
|
|
|
void FilePath_selfTest (void)
|
|
{
|
|
DE_TEST_ASSERT(!FilePath(".").isAbsolutePath());
|
|
DE_TEST_ASSERT(!FilePath("..\\foo").isAbsolutePath());
|
|
DE_TEST_ASSERT(!FilePath("foo").isAbsolutePath());
|
|
DE_TEST_ASSERT(FilePath("\\foo/bar").isAbsolutePath());
|
|
DE_TEST_ASSERT(FilePath("/foo").isAbsolutePath());
|
|
DE_TEST_ASSERT(FilePath("\\").isAbsolutePath());
|
|
DE_TEST_ASSERT(FilePath("\\\\net\\loc").isAbsolutePath());
|
|
DE_TEST_ASSERT(FilePath("C:\\file.txt").isAbsolutePath());
|
|
DE_TEST_ASSERT(FilePath("c:/file.txt").isAbsolutePath());
|
|
|
|
DE_TEST_ASSERT(string(".") == FilePath(".//.").normalize().getPath());
|
|
DE_TEST_ASSERT(string(".") == FilePath(".").normalize().getPath());
|
|
DE_TEST_ASSERT((string("..") + FilePath::separator + "test") == FilePath("foo/../bar/../../test").normalize().getPath());
|
|
DE_TEST_ASSERT((FilePath::separator + "foo" + FilePath::separator + "foo.txt") == FilePath("/foo\\bar/..\\dir\\..\\foo.txt").normalize().getPath());
|
|
DE_TEST_ASSERT((string("c:") + FilePath::separator + "foo" + FilePath::separator + "foo.txt") == FilePath("c:/foo\\bar/..\\dir\\..\\foo.txt").normalize().getPath());
|
|
DE_TEST_ASSERT((FilePath::separator + FilePath::separator + "foo" + FilePath::separator + "foo.txt") == FilePath("\\\\foo\\bar/..\\dir\\..\\foo.txt").normalize().getPath());
|
|
|
|
DE_TEST_ASSERT(FilePath("foo/bar" ).getBaseName() == "bar");
|
|
DE_TEST_ASSERT(FilePath("foo/bar/" ).getBaseName() == "bar");
|
|
DE_TEST_ASSERT(FilePath("foo\\bar" ).getBaseName() == "bar");
|
|
DE_TEST_ASSERT(FilePath("foo\\bar\\" ).getBaseName() == "bar");
|
|
DE_TEST_ASSERT(FilePath("foo/bar" ).getDirName() == "foo");
|
|
DE_TEST_ASSERT(FilePath("foo/bar/" ).getDirName() == "foo");
|
|
DE_TEST_ASSERT(FilePath("foo\\bar" ).getDirName() == "foo");
|
|
DE_TEST_ASSERT(FilePath("foo\\bar\\" ).getDirName() == "foo");
|
|
DE_TEST_ASSERT(FilePath("/foo/bar/baz" ).getDirName() == FilePath::separator + "foo" + FilePath::separator + "bar");
|
|
}
|
|
|
|
static void createDirectoryImpl (const char* path)
|
|
{
|
|
#if (DE_OS == DE_OS_WIN32)
|
|
if (!CreateDirectory(path, DE_NULL))
|
|
throw std::runtime_error("Failed to create directory");
|
|
#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)
|
|
if (mkdir(path, 0777) != 0)
|
|
throw std::runtime_error("Failed to create directory");
|
|
#else
|
|
# error Implement createDirectoryImpl() for your platform.
|
|
#endif
|
|
}
|
|
|
|
void createDirectory (const char* path)
|
|
{
|
|
FilePath dirPath = FilePath::normalize(path);
|
|
FilePath parentPath (dirPath.getDirName());
|
|
|
|
if (dirPath.exists())
|
|
throw std::runtime_error("Destination exists already");
|
|
else if (!parentPath.exists())
|
|
throw std::runtime_error("Parent directory doesn't exist");
|
|
else if (parentPath.getType() != FilePath::TYPE_DIRECTORY)
|
|
throw std::runtime_error("Parent is not directory");
|
|
|
|
createDirectoryImpl(path);
|
|
}
|
|
|
|
void createDirectoryAndParents (const char* path)
|
|
{
|
|
std::vector<std::string> createPaths;
|
|
FilePath curPath (path);
|
|
|
|
if (curPath.exists())
|
|
throw std::runtime_error("Destination exists already");
|
|
|
|
while (!curPath.exists())
|
|
{
|
|
createPaths.push_back(curPath.getPath());
|
|
|
|
std::string parent = curPath.getDirName();
|
|
DE_CHECK_RUNTIME_ERR(parent != curPath.getPath());
|
|
curPath = FilePath(parent);
|
|
}
|
|
|
|
// Create in reverse order.
|
|
for (std::vector<std::string>::const_reverse_iterator parentIter = createPaths.rbegin(); parentIter != createPaths.rend(); parentIter++)
|
|
createDirectory(parentIter->c_str());
|
|
}
|
|
|
|
} // de
|