Home | History | Annotate | Download | only in decpp
      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) || (DE_OS == DE_OS_QNX)
    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