diff --git a/es-core/src/utils/FileSystemUtil.cpp b/es-core/src/utils/FileSystemUtil.cpp index b15f03766..1d282cb62 100644 --- a/es-core/src/utils/FileSystemUtil.cpp +++ b/es-core/src/utils/FileSystemUtil.cpp @@ -3,166 +3,528 @@ #include #include -#if defined(WIN32) +#if defined(_WIN32) // because windows... #include -#define snprintf _snprintf +#include #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 +#include +#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:: diff --git a/es-core/src/utils/FileSystemUtil.h b/es-core/src/utils/FileSystemUtil.h index d91b5666c..2d90d6951 100644 --- a/es-core/src/utils/FileSystemUtil.h +++ b/es-core/src/utils/FileSystemUtil.h @@ -2,21 +2,39 @@ #ifndef ES_CORE_UTILS_FILE_SYSTEM_UTIL_H #define ES_CORE_UTILS_FILE_SYSTEM_UTIL_H +#include #include 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 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:: diff --git a/es-core/src/utils/StringUtil.cpp b/es-core/src/utils/StringUtil.cpp index e1754e215..26a7c7c0b 100644 --- a/es-core/src/utils/StringUtil.cpp +++ b/es-core/src/utils/StringUtil.cpp @@ -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 diff --git a/es-core/src/utils/StringUtil.h b/es-core/src/utils/StringUtil.h index c745d82e4..fcbd84e79 100644 --- a/es-core/src/utils/StringUtil.h +++ b/es-core/src/utils/StringUtil.h @@ -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:: diff --git a/es-core/src/utils/TimeUtil.cpp b/es-core/src/utils/TimeUtil.cpp index 39f389464..5ae3690cb 100644 --- a/es-core/src/utils/TimeUtil.cpp +++ b/es-core/src/utils/TimeUtil.cpp @@ -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);