2020-06-22 15:27:53 +00:00
|
|
|
//
|
|
|
|
// FileSystemUtil.cpp
|
|
|
|
//
|
|
|
|
// Low-level filesystem functions.
|
2020-06-23 18:07:00 +00:00
|
|
|
// Resolve relative paths, resolve symlinks, create directories,
|
|
|
|
// remove files etc.
|
2020-06-22 15:27:53 +00:00
|
|
|
//
|
|
|
|
|
2018-08-21 13:38:07 +00:00
|
|
|
#define _FILE_OFFSET_BITS 64
|
|
|
|
|
2017-11-15 15:59:39 +00:00
|
|
|
#include "utils/FileSystemUtil.h"
|
2020-07-10 16:32:23 +00:00
|
|
|
|
|
|
|
#include "utils/StringUtil.h"
|
2020-07-09 17:26:48 +00:00
|
|
|
#include "Log.h"
|
2017-11-15 15:59:39 +00:00
|
|
|
|
|
|
|
#include <sys/stat.h>
|
2020-07-09 17:26:48 +00:00
|
|
|
#include <fstream>
|
2017-11-15 15:59:39 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2017-11-15 15:59:39 +00:00
|
|
|
#include <direct.h>
|
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
2017-12-04 23:32:04 +00:00
|
|
|
#include <Windows.h>
|
2020-07-03 18:23:51 +00:00
|
|
|
//#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
|
|
|
|
//#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
|
2020-06-22 15:27:53 +00:00
|
|
|
#else
|
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
2017-12-04 23:32:04 +00:00
|
|
|
#include <dirent.h>
|
|
|
|
#include <unistd.h>
|
2020-06-22 15:27:53 +00:00
|
|
|
#endif
|
2017-11-15 15:59:39 +00:00
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
// For Unix systems, try to get the install prefix as defined when CMake was run.
|
2020-06-24 15:38:41 +00:00
|
|
|
// The installPrefix directory is the value set for CMAKE_INSTALL_PREFIX during build.
|
|
|
|
// If not defined, the default prefix path '/usr/local' will be used.
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(__unix__)
|
2020-06-21 19:08:54 +00:00
|
|
|
#ifdef ES_INSTALL_PREFIX
|
|
|
|
std::string installPrefix = ES_INSTALL_PREFIX;
|
|
|
|
#else
|
|
|
|
std::string installPrefix = "/usr/local";
|
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
#endif
|
2020-06-21 19:08:54 +00:00
|
|
|
|
2017-11-15 15:59:39 +00:00
|
|
|
namespace Utils
|
|
|
|
{
|
2020-06-22 15:27:53 +00:00
|
|
|
namespace FileSystem
|
|
|
|
{
|
|
|
|
static std::string homePath = "";
|
|
|
|
static std::string exePath = "";
|
|
|
|
|
|
|
|
stringList getDirContent(const std::string& _path, const bool _recursive)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
stringList contentList;
|
|
|
|
|
|
|
|
// Only parse the directory, if it's a directory.
|
|
|
|
if (isDirectory(path)) {
|
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-22 15:27:53 +00:00
|
|
|
WIN32_FIND_DATAW findData;
|
2020-07-10 16:32:23 +00:00
|
|
|
std::wstring wildcard = Utils::String::stringToWideString(path) + L"/*";
|
|
|
|
HANDLE hFind = FindFirstFileW(wildcard.c_str(), &findData);
|
2020-06-22 15:27:53 +00:00
|
|
|
|
|
|
|
if (hFind != INVALID_HANDLE_VALUE) {
|
|
|
|
// Loop over all files in the directory.
|
|
|
|
do {
|
2020-07-10 16:32:23 +00:00
|
|
|
std::string name = Utils::String::wideStringToString(findData.cFileName);
|
2020-06-22 15:27:53 +00:00
|
|
|
// Ignore "." and ".."
|
|
|
|
if ((name != ".") && (name != "..")) {
|
|
|
|
std::string fullName(getGenericPath(path + "/" + name));
|
|
|
|
contentList.push_back(fullName);
|
|
|
|
|
|
|
|
if (_recursive && isDirectory(fullName))
|
|
|
|
contentList.merge(getDirContent(fullName, true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (FindNextFileW(hFind, &findData));
|
|
|
|
FindClose(hFind);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
DIR* dir = opendir(path.c_str());
|
|
|
|
|
2020-06-23 18:07:00 +00:00
|
|
|
if (dir != nullptr) {
|
2020-06-22 15:27:53 +00:00
|
|
|
struct dirent* entry;
|
|
|
|
// Loop over all files in the directory.
|
2020-06-23 18:07:00 +00:00
|
|
|
while ((entry = readdir(dir)) != nullptr) {
|
2020-06-22 15:27:53 +00:00
|
|
|
std::string name(entry->d_name);
|
|
|
|
|
|
|
|
// Ignore "." and ".."
|
|
|
|
if ((name != ".") && (name != "..")) {
|
|
|
|
std::string fullName(getGenericPath(path + "/" + name));
|
|
|
|
contentList.push_back(fullName);
|
|
|
|
|
|
|
|
if (_recursive && isDirectory(fullName))
|
|
|
|
contentList.merge(getDirContent(fullName, true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
contentList.sort();
|
|
|
|
return contentList;
|
|
|
|
}
|
|
|
|
|
|
|
|
stringList getPathList(const std::string& _path)
|
|
|
|
{
|
|
|
|
stringList pathList;
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
size_t start = 0;
|
|
|
|
size_t end = 0;
|
|
|
|
|
|
|
|
// Split at '/'
|
|
|
|
while ((end = path.find("/", start)) != std::string::npos) {
|
|
|
|
if (end != start)
|
|
|
|
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));
|
|
|
|
|
|
|
|
return pathList;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setHomePath(const std::string& _path)
|
|
|
|
{
|
|
|
|
homePath = getGenericPath(_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getHomePath()
|
|
|
|
{
|
|
|
|
// Only construct the homepath once.
|
|
|
|
if (homePath.length())
|
|
|
|
return homePath;
|
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-22 15:27:53 +00:00
|
|
|
// On Windows we need to check HOMEDRIVE and HOMEPATH.
|
|
|
|
if (!homePath.length()) {
|
2020-07-10 16:32:23 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
std::string envHomeDrive =
|
|
|
|
Utils::String::wideStringToString(_wgetenv(L"HOMEDRIVE"));
|
|
|
|
std::string envHomePath =
|
|
|
|
Utils::String::wideStringToString(_wgetenv(L"HOMEPATH"));
|
|
|
|
#else
|
2020-06-22 15:27:53 +00:00
|
|
|
std::string envHomeDrive = getenv("HOMEDRIVE");
|
|
|
|
std::string envHomePath = getenv("HOMEPATH");
|
2020-07-10 16:32:23 +00:00
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
if (envHomeDrive.length() && envHomePath.length())
|
|
|
|
homePath = getGenericPath(envHomeDrive + "/" + envHomePath);
|
|
|
|
}
|
2020-07-03 18:23:51 +00:00
|
|
|
#else
|
|
|
|
// Check for HOME environment variable.
|
|
|
|
if (!homePath.length()) {
|
|
|
|
std::string envHome = getenv("HOME");
|
|
|
|
if (envHome.length())
|
|
|
|
homePath = getGenericPath(envHome);
|
|
|
|
}
|
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
|
|
|
|
// No homepath found, fall back to current working directory.
|
|
|
|
if (!homePath.length())
|
|
|
|
homePath = getCWDPath();
|
|
|
|
|
|
|
|
return homePath;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getCWDPath()
|
|
|
|
{
|
|
|
|
char temp[512];
|
|
|
|
|
|
|
|
// Return current working directory.
|
2020-07-10 16:32:23 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
wchar_t tempWide[512];
|
|
|
|
return (_wgetcwd(tempWide, 512) ?
|
|
|
|
getGenericPath(Utils::String::wideStringToString(tempWide)) : "");
|
|
|
|
#else
|
2020-06-22 15:27:53 +00:00
|
|
|
return (getcwd(temp, 512) ? getGenericPath(temp) : "");
|
2020-07-10 16:32:23 +00:00
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
}
|
|
|
|
|
2020-07-10 17:53:33 +00:00
|
|
|
std::string getPathToBinary(const std::string& executable)
|
|
|
|
{
|
|
|
|
#ifdef _WIN64
|
|
|
|
return "";
|
|
|
|
#else
|
|
|
|
std::string pathVariable = std::string(getenv("PATH"));
|
|
|
|
std::vector<std::string> pathList =
|
|
|
|
Utils::String::delimitedStringToVector(pathVariable, ":");
|
|
|
|
|
|
|
|
std::string pathTest;
|
|
|
|
|
|
|
|
for (auto it = pathList.cbegin(); it != pathList.cend(); it++) {
|
|
|
|
pathTest = it->c_str() + ("/" + executable);
|
|
|
|
if (exists(pathTest))
|
|
|
|
return it->c_str();
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-06-22 15:27:53 +00:00
|
|
|
void setExePath(const std::string& _path)
|
|
|
|
{
|
|
|
|
constexpr int path_max = 32767;
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-22 15:27:53 +00:00
|
|
|
std::wstring result(path_max, 0);
|
|
|
|
if (GetModuleFileNameW(nullptr, &result[0], path_max) != 0)
|
2020-07-10 16:32:23 +00:00
|
|
|
exePath = Utils::String::wideStringToString(result);
|
2020-06-22 15:27:53 +00:00
|
|
|
#else
|
|
|
|
std::string result(path_max, 0);
|
|
|
|
if (readlink("/proc/self/exe", &result[0], path_max) != -1)
|
|
|
|
exePath = result;
|
|
|
|
#endif
|
|
|
|
exePath = getCanonicalPath(exePath);
|
|
|
|
|
|
|
|
// Fallback to argv[0] if everything else fails.
|
|
|
|
if (exePath.empty())
|
|
|
|
exePath = getCanonicalPath(_path);
|
|
|
|
if (isRegularFile(exePath))
|
|
|
|
exePath = getParent(exePath);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getExePath()
|
|
|
|
{
|
|
|
|
return exePath;
|
|
|
|
}
|
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
std::string getProgramDataPath()
|
2020-06-22 15:27:53 +00:00
|
|
|
{
|
2020-07-03 18:23:51 +00:00
|
|
|
// For Unix systems, installPrefix should be populated by CMAKE from
|
|
|
|
// $CMAKE_INSTALL_PREFIX. But just as a precaution, let's check if this
|
|
|
|
// variable is blank, and if so set it to '/usr/local'.
|
|
|
|
// Just in case some build environments won't handle this correctly.
|
|
|
|
// For Windows it doesn't really work like that and the application could have
|
|
|
|
// been install to an arbitrary location, so this function won't be used on that OS.
|
|
|
|
#ifdef __unix__
|
2020-06-22 15:27:53 +00:00
|
|
|
if (!installPrefix.length())
|
|
|
|
installPrefix = "/usr/local";
|
2020-07-03 18:23:51 +00:00
|
|
|
return installPrefix + "/share/emulationstation";
|
|
|
|
#else
|
|
|
|
return "";
|
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string getPreferredPath(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = _path;
|
|
|
|
size_t offset = std::string::npos;
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-22 15:27:53 +00:00
|
|
|
// Convert '/' to '\\'
|
|
|
|
while ((offset = path.find('/')) != std::string::npos)
|
|
|
|
path.replace(offset, 1, "\\");
|
|
|
|
#endif
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getGenericPath(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);
|
|
|
|
|
|
|
|
// Remove trailing '/' when the path is more than a simple '/'
|
|
|
|
while (path.length() > 1 && ((offset = path.find_last_of('/')) == (path.length() - 1)))
|
|
|
|
path.erase(offset, 1);
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getEscapedPath(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-22 15:27:53 +00:00
|
|
|
// Windows escapes stuff by just putting everything in quotes.
|
|
|
|
return '"' + getPreferredPath(path) + '"';
|
|
|
|
#else
|
|
|
|
// Insert a backslash before most characters that would mess up a bash path.
|
|
|
|
const char* invalidChars = "\\ '\"!$^&*(){}[]?;<>";
|
|
|
|
const char* invalidChar = invalidChars;
|
|
|
|
|
|
|
|
while (*invalidChar) {
|
|
|
|
size_t start = 0;
|
|
|
|
size_t offset = 0;
|
|
|
|
|
|
|
|
while ((offset = path.find(*invalidChar, start)) != std::string::npos) {
|
|
|
|
start = offset + 1;
|
|
|
|
|
|
|
|
if ((offset == 0) || (path[offset - 1] != '\\')) {
|
|
|
|
path.insert(offset, 1, '\\');
|
|
|
|
++start;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++invalidChar;
|
|
|
|
}
|
|
|
|
return path;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getCanonicalPath(const std::string& _path)
|
|
|
|
{
|
|
|
|
// Hack for builtin resources.
|
|
|
|
if ((_path[0] == ':') && (_path[1] == '/'))
|
|
|
|
return _path;
|
|
|
|
|
|
|
|
std::string path = exists(_path) ? getAbsolutePath(_path) : getGenericPath(_path);
|
|
|
|
|
|
|
|
// Cleanup path.
|
|
|
|
bool scan = true;
|
|
|
|
while (scan) {
|
|
|
|
stringList pathList = getPathList(path);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-22 15:27:53 +00:00
|
|
|
// Append folder to path.
|
|
|
|
path += (path.size() == 0) ? (*it) : ("/" + (*it));
|
|
|
|
#else
|
|
|
|
// Append folder to path.
|
|
|
|
path += ("/" + (*it));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// 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 path;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getAbsolutePath(const std::string& _path, const std::string& _base)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
std::string base = isAbsolute(_base) ? getGenericPath(_base) : getAbsolutePath(_base);
|
|
|
|
|
|
|
|
// Return absolute path.
|
|
|
|
return isAbsolute(path) ? path : getGenericPath(base + "/" + path);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getParent(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getFileName(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
size_t offset = std::string::npos;
|
|
|
|
|
|
|
|
// Find last '/' and return the filename.
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getStem(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string fileName = getFileName(_path);
|
|
|
|
size_t offset = std::string::npos;
|
|
|
|
|
|
|
|
// Empty fileName.
|
|
|
|
if (fileName == ".")
|
|
|
|
return fileName;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 ".";
|
|
|
|
}
|
|
|
|
|
2020-07-10 18:58:53 +00:00
|
|
|
std::string expandHomePath(const std::string& _path)
|
|
|
|
{
|
|
|
|
// Expand home path if ~ is used.
|
|
|
|
std::string expandedPath = _path;
|
|
|
|
|
|
|
|
expandedPath = Utils::String::replace(_path, "~", Utils::FileSystem::getHomePath());
|
|
|
|
return expandedPath;
|
|
|
|
}
|
|
|
|
|
2020-06-22 15:27:53 +00:00
|
|
|
std::string resolveRelativePath(const std::string& _path,
|
|
|
|
const std::string& _relativeTo, const bool _allowHome)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
std::string relativeTo = isDirectory(_relativeTo) ?
|
|
|
|
getGenericPath(_relativeTo) : getParent(_relativeTo);
|
|
|
|
|
|
|
|
// Nothing to resolve.
|
|
|
|
if (!path.length())
|
|
|
|
return path;
|
|
|
|
|
|
|
|
// Replace '.' with relativeTo.
|
|
|
|
if ((path[0] == '.') && (path[1] == '/'))
|
|
|
|
return (relativeTo + &(path[1]));
|
|
|
|
|
|
|
|
// Replace '~' with homePath.
|
|
|
|
if (_allowHome && (path[0] == '~') && (path[1] == '/'))
|
|
|
|
return (getHomePath() + &(path[1]));
|
|
|
|
|
|
|
|
// Nothing to resolve.
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string createRelativePath(const std::string& _path,
|
|
|
|
const std::string& _relativeTo, const bool _allowHome)
|
|
|
|
{
|
|
|
|
bool contains = false;
|
|
|
|
std::string path = removeCommonPath(_path, _relativeTo, contains);
|
|
|
|
|
|
|
|
if (contains)
|
|
|
|
return ("./" + path);
|
|
|
|
|
|
|
|
if (_allowHome) {
|
|
|
|
path = removeCommonPath(_path, getHomePath(), contains);
|
|
|
|
|
|
|
|
if (contains)
|
|
|
|
return ("~/" + path);
|
|
|
|
}
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string removeCommonPath(const std::string& _path,
|
|
|
|
const std::string& _common, bool& _contains)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
std::string common = isDirectory(_common) ?
|
|
|
|
getGenericPath(_common) : getParent(_common);
|
|
|
|
|
|
|
|
// Check if path contains common.
|
|
|
|
if (path.find(common) == 0) {
|
|
|
|
_contains = true;
|
|
|
|
return path.substr(common.length() + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// It didn't.
|
|
|
|
_contains = false;
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string resolveSymlink(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
std::string resolved;
|
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-22 15:27:53 +00:00
|
|
|
HANDLE hFile = CreateFile(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
|
|
|
|
0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
2020-07-08 15:01:47 +00:00
|
|
|
// TEMPORARY, will need to fix this later.
|
|
|
|
// if (hFile != INVALID_HANDLE_VALUE) {
|
|
|
|
// resolved.resize(GetFinalPathNameByHandle(hFile, nullptr, 0,
|
|
|
|
// resolved.resize(GetFinalPathNameByHandle(hFile, nullptr, 0,
|
|
|
|
// FILE_NAME_NORMALIZED) + 1);
|
|
|
|
// if (GetFinalPathNameByHandle(hFile, (LPSTR)resolved.data(),
|
|
|
|
// if (GetFinalPathNameByHandle(hFile, (LPSTR)resolved.data(),
|
|
|
|
// (DWORD)resolved.size(), FILE_NAME_NORMALIZED) > 0) {
|
|
|
|
// resolved.resize(resolved.size() - 1);
|
|
|
|
// resolved = getGenericPath(resolved);
|
|
|
|
// }
|
|
|
|
// CloseHandle(hFile);
|
|
|
|
// }
|
2020-06-22 15:27:53 +00:00
|
|
|
#else
|
|
|
|
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 = getGenericPath(resolved);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return resolved;
|
|
|
|
}
|
|
|
|
|
2020-07-09 17:26:48 +00:00
|
|
|
bool copyFile(const std::string& _source_path,
|
|
|
|
const std::string& _destination_path, bool _overwrite)
|
|
|
|
{
|
|
|
|
if (!exists(_source_path)) {
|
|
|
|
LOG(LogError) << "Error - Can't copy file, source file does not exist:";
|
|
|
|
LOG(LogError) << _source_path;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-13 18:58:25 +00:00
|
|
|
if (isDirectory(_destination_path)) {
|
2020-07-09 17:26:48 +00:00
|
|
|
LOG(LogError) << "Error - Destination file is actually a directory:";
|
|
|
|
LOG(LogError) << _destination_path;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_overwrite && exists(_destination_path)) {
|
|
|
|
LOG(LogError) << "Error - Destination file exists and the overwrite flag "
|
|
|
|
"has not been set.";
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-10 16:32:23 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
std::ifstream sourceFile(Utils::String::stringToWideString(_source_path).c_str(),
|
|
|
|
std::ios::binary);
|
|
|
|
#else
|
2020-07-09 17:26:48 +00:00
|
|
|
std::ifstream sourceFile(_source_path, std::ios::binary);
|
2020-07-10 16:32:23 +00:00
|
|
|
#endif
|
2020-07-09 17:26:48 +00:00
|
|
|
|
|
|
|
if (sourceFile.fail()) {
|
|
|
|
LOG(LogError) << "Error - Couldn't read from source file (" << _source_path <<
|
|
|
|
"), permission problems?";
|
|
|
|
sourceFile.close();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-10 16:32:23 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
std::ofstream targetFile(Utils::String::stringToWideString(_destination_path).c_str(),
|
|
|
|
std::ios::binary);
|
|
|
|
#else
|
2020-07-09 17:26:48 +00:00
|
|
|
std::ofstream targetFile(_destination_path, std::ios::binary);
|
2020-07-10 16:32:23 +00:00
|
|
|
#endif
|
2020-07-09 17:26:48 +00:00
|
|
|
|
|
|
|
if (targetFile.fail()) {
|
|
|
|
LOG(LogError) << "Error - Couldn't write to target file (" << _destination_path <<
|
|
|
|
"), permission problems?";
|
|
|
|
targetFile.close();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
targetFile << sourceFile.rdbuf();
|
|
|
|
|
|
|
|
sourceFile.close();
|
|
|
|
targetFile.close();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-10 16:32:23 +00:00
|
|
|
bool renameFile(const std::string& _source_path,
|
|
|
|
const std::string& _destination_path, bool _overwrite)
|
|
|
|
{
|
|
|
|
// Don't print any error message for a missing source file as Log will use this
|
|
|
|
// function when initializing the logging. It would always generate an error in
|
|
|
|
// case it's the first application start (as an old log file would then not exist).
|
|
|
|
if (!exists(_source_path)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-13 18:58:25 +00:00
|
|
|
if (isDirectory(_destination_path)) {
|
2020-07-10 16:32:23 +00:00
|
|
|
LOG(LogError) << "Error - Destination file is actually a directory:";
|
|
|
|
LOG(LogError) << _destination_path;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_overwrite && exists(_destination_path)) {
|
|
|
|
LOG(LogError) << "Error - Destination file exists and the overwrite flag "
|
|
|
|
"has not been set.";
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _WIN64
|
|
|
|
_wrename(Utils::String::stringToWideString(_source_path).c_str(),
|
|
|
|
Utils::String::stringToWideString(_destination_path).c_str());
|
|
|
|
#else
|
|
|
|
std::rename(_source_path.c_str(), _destination_path.c_str());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-06-22 15:27:53 +00:00
|
|
|
bool removeFile(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
|
|
|
|
// Don't remove if it doesn't exists.
|
|
|
|
if (!exists(path))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Try to remove file.
|
2020-07-10 16:32:23 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
if (_wunlink(Utils::String::stringToWideString(path).c_str()) != 0) {
|
|
|
|
LOG(LogError) << "Error - Couldn't delete file, permission problems?";
|
|
|
|
LOG(LogError) << path;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (unlink(path.c_str()) != 0) {
|
|
|
|
LOG(LogError) << "Error - Couldn't delete file, permission problems?";
|
|
|
|
LOG(LogError) << path;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
2020-06-22 15:27:53 +00:00
|
|
|
return (unlink(path.c_str()) == 0);
|
2020-07-10 16:32:23 +00:00
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool createDirectory(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
|
|
|
|
// Don't create if it already exists.
|
|
|
|
if (exists(path))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Try to create directory.
|
2020-07-10 16:32:23 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
if (_wmkdir(Utils::String::stringToWideString(path).c_str()) == 0)
|
|
|
|
return true;
|
|
|
|
#else
|
2020-06-22 15:27:53 +00:00
|
|
|
if (mkdir(path.c_str(), 0755) == 0)
|
|
|
|
return true;
|
2020-07-10 16:32:23 +00:00
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
|
|
|
|
// 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.
|
2020-07-10 16:32:23 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
return (_wmkdir(Utils::String::stringToWideString(path).c_str()) == 0);
|
|
|
|
#else
|
2020-06-22 15:27:53 +00:00
|
|
|
return (mkdir(path.c_str(), 0755) == 0);
|
2020-07-10 16:32:23 +00:00
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool exists(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
|
2020-07-10 16:32:23 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
struct _stat64 info;
|
|
|
|
return (_wstat64(Utils::String::stringToWideString(path).c_str(), &info) == 0);
|
|
|
|
#else
|
|
|
|
struct stat64 info;
|
2020-06-22 15:27:53 +00:00
|
|
|
return (stat64(path.c_str(), &info) == 0);
|
2020-07-10 16:32:23 +00:00
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isAbsolute(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-22 15:27:53 +00:00
|
|
|
return ((path.size() > 1) && (path[1] == ':'));
|
|
|
|
#else
|
|
|
|
return ((path.size() > 0) && (path[0] == '/'));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isRegularFile(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
struct stat64 info;
|
|
|
|
|
2020-07-10 16:32:23 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
if (_wstat64(Utils::String::stringToWideString(path).c_str(), &info) != 0)
|
|
|
|
return false;
|
|
|
|
#else
|
2020-06-22 15:27:53 +00:00
|
|
|
if (stat64(path.c_str(), &info) != 0)
|
|
|
|
return false;
|
2020-07-10 16:32:23 +00:00
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
|
|
|
|
// Check for S_IFREG attribute.
|
|
|
|
return (S_ISREG(info.st_mode));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isDirectory(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
2020-07-10 16:32:23 +00:00
|
|
|
struct stat64 info;
|
2020-06-22 15:27:53 +00:00
|
|
|
|
2020-07-10 16:32:23 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
if (_wstat64(Utils::String::stringToWideString(path).c_str(), &info) != 0)
|
|
|
|
return false;
|
|
|
|
#else
|
|
|
|
if (stat64(path.c_str(), &info) != 0)
|
2020-06-22 15:27:53 +00:00
|
|
|
return false;
|
2020-07-10 16:32:23 +00:00
|
|
|
#endif
|
2020-06-22 15:27:53 +00:00
|
|
|
|
|
|
|
// Check for S_IFDIR attribute.
|
|
|
|
return (S_ISDIR(info.st_mode));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isSymlink(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-22 15:27:53 +00:00
|
|
|
// Check for symlink attribute.
|
|
|
|
const DWORD Attributes = GetFileAttributes(path.c_str());
|
|
|
|
if ((Attributes != INVALID_FILE_ATTRIBUTES) &&
|
|
|
|
(Attributes & FILE_ATTRIBUTE_REPARSE_POINT))
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
struct stat info;
|
|
|
|
|
|
|
|
if (lstat(path.c_str(), &info) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Check for S_IFLNK attribute.
|
|
|
|
return (S_ISLNK(info.st_mode));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Not a symlink.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isHidden(const std::string& _path)
|
|
|
|
{
|
|
|
|
std::string path = getGenericPath(_path);
|
|
|
|
|
2020-07-03 18:23:51 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-22 15:27:53 +00:00
|
|
|
// Check for hidden attribute.
|
|
|
|
const DWORD Attributes = GetFileAttributes(path.c_str());
|
|
|
|
if ((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN))
|
|
|
|
return true;
|
2020-07-03 18:23:51 +00:00
|
|
|
#endif // _WIN64
|
2020-06-22 15:27:53 +00:00
|
|
|
|
|
|
|
// Filenames starting with . are hidden in Linux, but
|
|
|
|
// we do this check for windows as well.
|
|
|
|
if (getFileName(path)[0] == '.')
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // FileSystem::
|
2017-11-15 15:59:39 +00:00
|
|
|
} // Utils::
|