Add getDirContent, getHomePath, getCWDPath, canonicalPath, absolutePath, resolvePath, resolveSymlink, getExtension, removeFile, isAbsolute, isRegularFile, isDirectory, isSymlink, isHidden and isEquivalent

Rename makeGeneric to genericPath and escapePath to escapedPath

Add toUpper
This commit is contained in:
Tomas Jakobsson 2017-12-05 00:32:04 +01:00
parent 05caef2f28
commit 18d6b9341e
5 changed files with 505 additions and 113 deletions

View file

@ -3,166 +3,528 @@
#include <sys/stat.h>
#include <string.h>
#if defined(WIN32)
#if defined(_WIN32)
// because windows...
#include <direct.h>
#define snprintf _snprintf
#include <Windows.h>
#define mkdir(x,y) _mkdir(x)
#endif // WIN32
#define snprintf _snprintf
#define unlink _unlink
#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#else // _WIN32
#include <dirent.h>
#include <unistd.h>
#endif // _WIN32
namespace Utils
{
namespace FileSystem
{
bool createDirectory(const std::string& _path)
stringList getDirContent(const std::string& _path)
{
// don't create if it already exists
if(exists(_path))
return true;
std::string path = genericPath(_path);
stringList contentList;
// convert '\\' to '/'
makeGeneric(_path);
// try to create directory
if(mkdir(_path.c_str(), 0755) == 0)
return true;
// failed to create directory, try to create the parent
std::string parent = getParent(_path);
// only try to create parent if it's not identical to path
if(parent != _path)
createDirectory(parent);
// try to create directory again now that the parent should exist
return (mkdir(_path.c_str(), 0755) == 0);
} // createDirectory
void makeGeneric(const std::string& _path)
{
char* p = nullptr;
// convert '\\' to '/'
for(p = (char*)_path.c_str() + 1; *p; ++p)
// only parse the directory, if it's a directory
if(isDirectory(path))
{
if(*p == '\\')
*p = '/';
#if defined(_WIN32)
WIN32_FIND_DATA findData;
HANDLE hFind = FindFirstFile((path + "/*").c_str(), &findData);
if(hFind != INVALID_HANDLE_VALUE)
{
// loop over all files in the directory
do
{
std::string name(findData.cFileName);
// ignore "." and ".."
if((name != ".") && (name != ".."))
contentList.push_back(name);
}
while(FindNextFile(hFind, &findData));
FindClose(hFind);
}
#else // _WIN32
DIR* dir = opendir(path.c_str());
if(dir != NULL)
{
struct dirent* entry;
// loop over all files in the directory
while((entry = readdir(dir)) != NULL)
{
std::string name(entry->d_name);
// ignore "." and ".."
if((name != ".") && (name != ".."))
contentList.push_back(name);
}
closedir(dir);
}
#endif // _WIN32
}
} // makeGeneric
// sort the content list
contentList.sort();
std::string escapePath(const std::string& _path)
// return the content list
return contentList;
} // getDirContent
std::string getHomePath()
{
static std::string path;
#ifdef WIN32
// only construct the homepath once
if(!path.length())
{
// this should give us something like "/home/YOUR_USERNAME" on Linux and "C:/Users/YOUR_USERNAME/" on Windows
std::string envHome(getenv("HOME"));
if(envHome.length())
path = genericPath(envHome);
#if defined(_WIN32)
// but does not seem to work for Windows XP or Vista, so try something else
if(!path.length())
{
std::string envDir(getenv("HOMEDRIVE"));
std::string envPath(getenv("HOMEPATH"));
if(envDir.length() && envPath.length())
path = genericPath(envDir + "/" + envPath);
}
#endif // _WIN32
}
// return constructed homepath
return path;
} // getHomePath
std::string getCWDPath()
{
char temp[512];
// return current working directory path
return (getcwd(temp, 512) ? genericPath(temp) : "");
} // getCWDPath
std::string genericPath(const std::string& _path)
{
std::string path = _path;
size_t offset = std::string::npos;
// remove "\\\\?\\"
if((path.find("\\\\?\\")) == 0)
path.erase(0, 4);
// convert '\\' to '/'
while((offset = path.find('\\')) != std::string::npos)
path.replace(offset, 1 ,"/");
// remove double '/'
while((offset = path.find("//")) != std::string::npos)
path.erase(offset, 1);
// return generic path
return path;
} // genericPath
std::string escapedPath(const std::string& _path)
{
std::string path = genericPath(_path);
#if defined(_WIN32)
// windows escapes stuff by just putting everything in quotes
return '"' + _path + '"';
#else // WIN32
return '"' + path + '"';
#else // _WIN32
// insert a backslash before most characters that would mess up a bash path
std::string escapedPath = _path;
const char* invalidChars = "\\ '\"!$^&*(){}[]?;<>";
const char* invalidChar = invalidChars;
size_t offset = std::string::npos;
while(*invalidChar)
{
for(size_t i = 0; i < escapedPath.length(); ++i)
{
if(escapedPath[i] == *invalidChar)
{
escapedPath.insert(i, 1, '\\');
++i;
}
}
while((offset = path.find(*invalidChar)) != std::string::npos)
path.insert(offset, 1, '\\');
++invalidChar;
}
return escapedPath;
#endif // WIN32
// return escaped path
return path;
#endif // _WIN32
} // escapePath
} // escapedPath
std::string getParent(const std::string& _path)
std::string canonicalPath(const std::string& _path)
{
// convert '\\' to '/'
makeGeneric(_path);
std::string path = absolutePath(_path);
// make a copy of the path
char temp[512];
size_t len = snprintf(temp, sizeof(temp), "%s", _path.c_str());
// find last '/' and end the new path
while(len > 1)
// cleanup path
bool scan = true;
while(scan)
{
if(temp[--len] == '/')
stringList pathList;
size_t start = 0;
size_t end = 0;
// split at '/'
while((end = path.find("/", start)) != std::string::npos)
{
temp[len] = 0;
return temp;
pathList.push_back(std::string(path, start, end - start));
start = end + 1;
}
// add last folder / file to pathList
if(start != path.size())
pathList.push_back(std::string(path, start, path.size() - start));
path.clear();
scan = false;
for(stringList::const_iterator it = pathList.cbegin(); it != pathList.cend(); ++it)
{
// ignore empty
if((*it).empty())
continue;
// remove "/./"
if((*it) == ".")
continue;
// resolve "/../"
if((*it) == "..")
{
path = getParent(path);
continue;
}
#if defined(_WIN32)
// append folder to path
path += (path.size() == 0) ? (*it) : ("/" + (*it));
#else // _WIN32
// append folder to path
path += ("/" + (*it));
#endif // _WIN32
// resolve symlink
if(isSymlink(path))
{
std::string resolved = resolveSymlink(path);
if(resolved.empty())
return "";
if(isAbsolute(resolved))
path = resolved;
else
path = getParent(path) + "/" + resolved;
for( ++it; it != pathList.cend(); ++it)
path += (path.size() == 0) ? (*it) : ("/" + (*it));
scan = true;
break;
}
}
}
// return canonical path
return path;
} // canonicalPath
std::string absolutePath(const std::string& _path, const std::string& _base)
{
std::string path = genericPath(_path);
std::string base = isAbsolute(_base) ? genericPath(_base) : absolutePath(_base);
// return absolute path
return isAbsolute(path) ? path : genericPath(base + "/" + path);
} // absolutePath
std::string resolvePath(const std::string& _path, const std::string& _relativeTo, const bool _allowHome)
{
std::string path = genericPath(_path);
std::string relativeTo = isDirectory(_relativeTo) ? _relativeTo : getParent(_relativeTo);
// nothing to resolve
if(!path.length())
return path;
// replace '.' with relativeTo
if(path[0] == '.')
return genericPath(relativeTo + "/" + &(path[1]));
// replace '~' with homePath
if(_allowHome && (path[0] == '~'))
return genericPath(getHomePath() + "/" + &(path[1]));
// nothing to resolve
return path;
} // resolvePath
std::string resolveSymlink(const std::string& _path)
{
std::string path = genericPath(_path);
std::string resolved;
#if defined(_WIN32)
HANDLE hFile = CreateFile(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if(hFile != INVALID_HANDLE_VALUE)
{
resolved.resize(GetFinalPathNameByHandle(hFile, nullptr, 0, FILE_NAME_NORMALIZED) + 1);
if(GetFinalPathNameByHandle(hFile, (LPSTR)resolved.data(), (DWORD)resolved.size(), FILE_NAME_NORMALIZED) > 0)
{
resolved.resize(resolved.size() - 1);
resolved = genericPath(resolved);
}
CloseHandle(hFile);
}
#else // _WIN32
struct stat info;
// check if lstat succeeded
if(lstat(path.c_str(), &info) == 0)
{
resolved.resize(info.st_size);
if(readlink(path.c_str(), (char*)resolved.data(), resolved.size()) > 0)
resolved = genericPath(resolved);
}
#endif // _WIN32
// return resolved path
return resolved;
} // resolveSymlink
std::string getParent(const std::string& _path)
{
std::string path = genericPath(_path);
size_t offset = std::string::npos;
// find last '/' and erase it
if((offset = path.find_last_of('/')) != std::string::npos)
return path.erase(offset);
// no parent found
return _path;
return path;
} // getParent
std::string getFileName(const std::string& _path)
{
// convert '\\' to '/'
makeGeneric(_path);
// make a copy of the path
char temp[512];
size_t len = snprintf(temp, sizeof(temp), "%s", _path.c_str());
std::string path = genericPath(_path);
size_t offset = std::string::npos;
// find last '/' and return the filename
while(len > 1)
{
// return "." if this is the end of the path, otherwise return filename
if(temp[--len] == '/')
return ((temp[len + 1] == 0) ? "." : (temp + len + 1));
}
if((offset = path.find_last_of('/')) != std::string::npos)
return ((path[offset + 1] == 0) ? "." : std::string(path, offset + 1));
// no '/' found, entire path is a filename
return _path;
return path;
} // getFileName
std::string getStem(const std::string& _path)
{
std::string fileName = getFileName(_path);
size_t offset = std::string::npos;
// empty fileName
if(fileName == ".")
return fileName;
// make a copy of the filename
char temp[512];
size_t len = snprintf(temp, sizeof(temp), "%s", fileName.c_str());
// find last '.' and remove the extension
while(len > 1)
{
if(temp[--len] == '.')
{
temp[len] = 0;
return temp;
}
}
// find last '.' and erase the extension
if((offset = fileName.find_last_of('.')) != std::string::npos)
return fileName.erase(offset);
// no '.' found, filename has no extension
return fileName;
} // getStem
std::string getExtension(const std::string& _path)
{
std::string fileName = getFileName(_path);
size_t offset = std::string::npos;
// empty fileName
if(fileName == ".")
return fileName;
// find last '.' and return the extension
if((offset = fileName.find_last_of('.')) != std::string::npos)
return std::string(fileName, offset);
// no '.' found, filename has no extension
return ".";
} // getExtension
bool removeFile(const std::string& _path)
{
std::string path = genericPath(_path);
// don't remove if it doesn't exists
if(!exists(path))
return true;
// try to remove file
return (unlink(path.c_str()) == 0);
} // removeFile
bool createDirectory(const std::string& _path)
{
std::string path = genericPath(_path);
// don't create if it already exists
if(exists(path))
return true;
// try to create directory
if(mkdir(path.c_str(), 0755) == 0)
return true;
// failed to create directory, try to create the parent
std::string parent = getParent(path);
// only try to create parent if it's not identical to path
if(parent != path)
createDirectory(parent);
// try to create directory again now that the parent should exist
return (mkdir(path.c_str(), 0755) == 0);
} // createDirectory
bool exists(const std::string& _path)
{
std::string path = genericPath(_path);
struct stat info;
return (stat(_path.c_str(), &info) == 0);
// check if stat succeeded
return (stat(path.c_str(), &info) == 0);
} // exists
bool isAbsolute(const std::string& _path)
{
std::string path = genericPath(_path);
#if defined(_WIN32)
return ((path.size() > 1) && (path[1] == ':'));
#else // _WIN32
return ((path.size() > 0) && (path[0] == '/'));
#endif // _WIN32
} // isAbsolute
bool isRegularFile(const std::string& _path)
{
std::string path = genericPath(_path);
struct stat info;
// check if stat succeeded
if(stat(path.c_str(), &info) != 0)
return false;
// check for S_IFREG attribute
return (S_ISREG(info.st_mode));
} // isRegularFile
bool isDirectory(const std::string& _path)
{
std::string path = genericPath(_path);
struct stat info;
// check if stat succeeded
if(stat(path.c_str(), &info) != 0)
return false;
// check for S_IFDIR attribute
return (S_ISDIR(info.st_mode));
} // isDirectory
bool isSymlink(const std::string& _path)
{
std::string path = genericPath(_path);
#if defined(_WIN32)
// check for symlink attribute
const DWORD Attributes = GetFileAttributes(path.c_str());
if((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_REPARSE_POINT))
return true;
#else // _WIN32
struct stat info;
// check if lstat succeeded
if(lstat(path.c_str(), &info) != 0)
return false;
// check for S_IFLNK attribute
return (S_ISLNK(info.st_mode));
#endif // _WIN32
// not a symlink
return false;
} // isSymlink
bool isHidden(const std::string& _path)
{
std::string path = genericPath(_path);
#if defined(_WIN32)
// check for hidden attribute
const DWORD Attributes = GetFileAttributes(path.c_str());
if((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN))
return true;
#endif // _WIN32
// filenames starting with . are hidden in linux, we do this check for windows as well
if(getFileName(path)[0] == '.')
return true;
// not hidden
return false;
} // isHidden
bool isEquivalent(const std::string& _path1, const std::string& _path2)
{
std::string path1 = genericPath(_path1);
std::string path2 = genericPath(_path2);
struct stat info1;
struct stat info2;
// check if stat succeeded
if((stat(path1.c_str(), &info1) != 0) || (stat(path2.c_str(), &info2) != 0))
return false;
// check if attributes are identical
return ((info1.st_dev == info2.st_dev) && (info1.st_ino == info2.st_ino) && (info1.st_size == info2.st_size) && (info1.st_mtime == info2.st_mtime));
} // isEquivalent
} // FileSystem::
} // Utils::

View file

@ -2,21 +2,39 @@
#ifndef ES_CORE_UTILS_FILE_SYSTEM_UTIL_H
#define ES_CORE_UTILS_FILE_SYSTEM_UTIL_H
#include <list>
#include <string>
namespace Utils
{
namespace FileSystem
{
bool createDirectory(const std::string& _path);
void makeGeneric (const std::string& _path);
std::string escapePath (const std::string& _path);
typedef std::list<std::string> stringList;
stringList getDirContent (const std::string& _path);
std::string getHomePath ();
std::string getCWDPath ();
std::string genericPath (const std::string& _path);
std::string escapedPath (const std::string& _path);
std::string canonicalPath (const std::string& _path);
std::string absolutePath (const std::string& _path, const std::string& _base = getCWDPath());
std::string resolvePath (const std::string& _path, const std::string& _relativeTo, const bool _allowHome);
std::string resolveSymlink (const std::string& _path);
std::string getParent (const std::string& _path);
std::string getFileName (const std::string& _path);
std::string getStem (const std::string& _path);
std::string getExtension (const std::string& _path);
bool removeFile (const std::string& _path);
bool createDirectory(const std::string& _path);
bool exists (const std::string& _path);
bool isAbsolute (const std::string& _path);
bool isRegularFile (const std::string& _path);
bool isDirectory (const std::string& _path);
bool isSymlink (const std::string& _path);
bool isHidden (const std::string& _path);
bool isEquivalent (const std::string& _path1, const std::string& _path2);
} // Utils::FileSystem::
} // FileSystem::
} // Utils::

View file

@ -132,15 +132,26 @@ namespace Utils
} // moveCursor
std::string trim(const std::string& _path)
std::string toUpper(const std::string& _string)
{
const size_t pathBegin = _path.find_first_not_of(" \t");
const size_t pathEnd = _path.find_last_not_of(" \t");
std::string string;
if(pathBegin == std::string::npos)
for(size_t i = 0; i < _string.length(); ++i)
string += (char)toupper(_string[i]);
return string;
} // toUpper
std::string trim(const std::string& _string)
{
const size_t strBegin = _string.find_first_not_of(" \t");
const size_t strEnd = _string.find_last_not_of(" \t");
if(strBegin == std::string::npos)
return "";
return _path.substr(pathBegin, pathEnd - pathBegin + 1);
return _string.substr(strBegin, strEnd - strBegin + 1);
} // trim

View file

@ -13,11 +13,12 @@ namespace Utils
size_t nextCursor (const std::string& _string, const size_t _cursor);
size_t prevCursor (const std::string& _string, const size_t _cursor);
size_t moveCursor (const std::string& _string, const size_t _cursor, const int _amount);
std::string trim (const std::string& _path);
std::string toUpper (const std::string& _string);
std::string trim (const std::string& _string);
bool startsWith (const std::string& _string, const std::string& _test);
bool endsWith (const std::string& _string, const std::string& _test);
} // Utils::String::
} // String::
} // Utils::

View file

@ -12,30 +12,30 @@ namespace Utils
mTimeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 };
mIsoString = "00000000T000000";
} // Time
} // DateTime::DateTime
DateTime::DateTime(const time_t& _time)
{
setTime(_time);
} // Time
} // DateTime::DateTime
DateTime::DateTime(const tm& _timeStruct)
{
setTimeStruct(_timeStruct);
} // Time
} // DateTime::DateTime
DateTime::DateTime(const std::string& _isoString)
{
setIsoString(_isoString);
} // Time
} // DateTime::DateTime
DateTime::~DateTime()
{
} // ~Time
} // DateTime::~DateTime
void DateTime::setTime(const time_t& _time)
{
@ -44,19 +44,19 @@ namespace Utils
mTimeStruct = *localtime(&mTime);
mIsoString = timeToString(mTime);
} // setTime
} // DateTime::setTime
void DateTime::setTimeStruct(const tm& _timeStruct)
{
setTime(mktime((tm*)&_timeStruct));
} // setTimeStruct
} // DateTime::setTimeStruct
void DateTime::setIsoString(const std::string& _isoString)
{
setTime(stringToTime(_isoString));
} // setIsoString
} // DateTime::setIsoString
Duration::Duration(const time_t& _time)
{
@ -66,12 +66,12 @@ namespace Utils
mMinutes = ((mTotalSeconds % (60*60)) - (mTotalSeconds % (60))) / 60;
mSeconds = mTotalSeconds % 60;
} // Duration
} // Duration::Duration
Duration::~Duration()
{
} // ~Duration
} // Duration::~Duration
time_t now()
{
@ -86,7 +86,7 @@ namespace Utils
const char* s = _string.c_str();
const char* f = _format.c_str();
tm timeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 };
int parsedChars = 0;
size_t parsedChars = 0;
if(_string == "not-a-date-time")
return mktime(&timeStruct);