ES-DE/es-core/src/utils/FileSystemUtil.cpp

881 lines
30 KiB
C++
Raw Normal View History

// SPDX-License-Identifier: MIT
2020-06-22 15:27:53 +00:00
//
// EmulationStation Desktop Edition
2020-06-22 15:27:53 +00:00
// FileSystemUtil.cpp
//
// Low-level filesystem functions.
// Resolve relative paths, resolve symlinks, create directories,
// remove files etc.
2020-06-22 15:27:53 +00:00
//
#define _FILE_OFFSET_BITS 64
#if defined(__APPLE__)
#define _DARWIN_USE_64_BIT_INODE
#endif
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "Log.h"
#include <fstream>
#include <string.h>
#include <sys/stat.h>
#if defined(_WIN64)
#include <direct.h>
#include <Windows.h>
#if defined(_MSC_VER) // MSVC compiler.
#define stat64 _stat64
#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif
2020-06-22 15:27:53 +00:00
#else
#include <dirent.h>
#include <unistd.h>
2020-06-22 15:27:53 +00:00
#endif
// For Unix systems, set the install prefix as defined via CMAKE_INSTALL_PREFIX when CMake was run.
// If not defined, the default prefix '/usr' will be used on Linux, '/usr/pkg' on NetBSD and
// '/usr/local' on FreeBSD and OpenBSD. This fallback should not be required though unless the
// build environment is broken.
#if defined(__unix__)
#if defined(ES_INSTALL_PREFIX)
std::string installPrefix = ES_INSTALL_PREFIX;
#else
#if defined(__linux__)
std::string installPrefix = "/usr";
#elif defined(__NetBSD__)
std::string installPrefix = "/usr/pkg";
#else
std::string installPrefix = "/usr/local";
#endif
2020-06-22 15:27:53 +00:00
#endif
#endif
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)) {
#if defined(_WIN64)
2020-06-22 15:27:53 +00:00
WIN32_FIND_DATAW findData;
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 {
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.sort();
2020-06-22 15:27:53 +00:00
contentList.merge(getDirContent(fullName, true));
}
2020-06-22 15:27:53 +00:00
}
}
while (FindNextFileW(hFind, &findData));
FindClose(hFind);
}
#else
DIR* dir = opendir(path.c_str());
if (dir != nullptr) {
2020-06-22 15:27:53 +00:00
struct dirent* entry;
// Loop over all files in the directory.
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.sort();
2020-06-22 15:27:53 +00:00
contentList.merge(getDirContent(fullName, true));
}
2020-06-22 15:27:53 +00:00
}
}
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;
#if defined(_WIN64)
2020-06-22 15:27:53 +00:00
// On Windows we need to check HOMEDRIVE and HOMEPATH.
if (!homePath.length()) {
std::wstring envHomeDrive;
std::wstring envHomePath;
#if defined(_MSC_VER) // MSVC compiler.
wchar_t* buffer;
if (!_wdupenv_s(&buffer, nullptr, L"HOMEDRIVE"))
envHomeDrive = buffer;
if (!_wdupenv_s(&buffer, nullptr, L"HOMEPATH"))
envHomePath = buffer;
#else
envHomeDrive = _wgetenv(L"HOMEDRIVE");
envHomePath = _wgetenv(L"HOMEPATH");
#endif
2020-06-22 15:27:53 +00:00
if (envHomeDrive.length() && envHomePath.length())
homePath = getGenericPath(Utils::String::wideStringToString(envHomeDrive) +
"/" + Utils::String::wideStringToString(envHomePath));
2020-06-22 15:27:53 +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()
{
// Return current working directory.
#if defined(_WIN64)
wchar_t tempWide[512];
return (_wgetcwd(tempWide, 512) ?
getGenericPath(Utils::String::wideStringToString(tempWide)) : "");
#else
2020-12-29 11:58:55 +00:00
char temp[512];
2020-06-22 15:27:53 +00:00
return (getcwd(temp, 512) ? getGenericPath(temp) : "");
#endif
2020-06-22 15:27:53 +00:00
}
2020-07-10 17:53:33 +00:00
std::string getPathToBinary(const std::string& executable)
{
#if defined(_WIN64)
2020-07-10 17:53:33 +00:00
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;
#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)
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;
}
std::string getProgramDataPath()
2020-06-22 15:27:53 +00:00
{
#if defined(__unix__)
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;
#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);
#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, '\\');
2021-01-25 17:07:11 +00:00
start++;
2020-06-22 15:27:53 +00:00
}
}
2021-01-25 17:07:11 +00:00
invalidChar++;
2020-06-22 15:27:53 +00:00
}
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();
2021-01-25 17:07:11 +00:00
it != pathList.cend(); it++) {
2020-06-22 15:27:53 +00:00
// Ignore empty.
if ((*it).empty())
continue;
// Remove "/./"
if ((*it) == ".")
continue;
// Resolve "/../"
if ((*it) == "..") {
path = getParent(path);
continue;
}
#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;
2021-01-25 17:07:11 +00:00
for (++it; it != pathList.cend(); it++)
2020-06-22 15:27:53 +00:00
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;
if (!Utils::FileSystem::isDirectory(_path)) {
// Find last '.' and erase the extension.
if ((offset = fileName.find_last_of('.')) != std::string::npos)
return fileName.erase(offset);
}
2020-06-22 15:27:53 +00:00
// 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 ".";
}
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);
2020-06-22 15:27:53 +00:00
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;
#if defined(_WIN64)
std::wstring resolvedW;
HANDLE hFile = CreateFileW(Utils::String::stringToWideString(path).c_str(),
FILE_READ_ATTRIBUTES, FILE_SHARE_READ, 0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile != INVALID_HANDLE_VALUE) {
resolvedW.resize(GetFinalPathNameByHandleW(hFile, nullptr, 0,
FILE_NAME_NORMALIZED) + 1);
if (GetFinalPathNameByHandleW(hFile, const_cast<LPWSTR>(resolvedW.data()),
static_cast<DWORD>(resolvedW.size()), FILE_NAME_NORMALIZED) > 0) {
resolvedW.resize(resolvedW.size() - 1);
resolved = getGenericPath(Utils::String::wideStringToString(resolvedW));
}
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(), const_cast<char*>(resolved.data()),
resolved.size()) > 0)
2020-06-22 15:27:53 +00:00
resolved = getGenericPath(resolved);
}
#endif
return resolved;
}
bool copyFile(const std::string& _source_path,
const std::string& _destination_path, bool _overwrite)
{
if (!exists(_source_path)) {
LOG(LogError) << "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)) {
LOG(LogError) << "Destination file is actually a directory:";
LOG(LogError) << _destination_path;
return true;
}
if (!_overwrite && exists(_destination_path)) {
LOG(LogError) << "Destination file exists and the overwrite flag "
"has not been set";
return true;
}
#if defined(_WIN64)
std::ifstream sourceFile(Utils::String::stringToWideString(_source_path).c_str(),
std::ios::binary);
#else
std::ifstream sourceFile(_source_path, std::ios::binary);
#endif
if (sourceFile.fail()) {
LOG(LogError) << "Couldn't read from source file \"" << _source_path <<
"\", permission problems?";
sourceFile.close();
return true;
}
#if defined(_WIN64)
std::ofstream targetFile(Utils::String::stringToWideString(_destination_path).c_str(),
std::ios::binary);
#else
std::ofstream targetFile(_destination_path, std::ios::binary);
#endif
if (targetFile.fail()) {
LOG(LogError) << "Couldn't write to target file \"" << _destination_path <<
"\", permission problems?";
targetFile.close();
return true;
}
targetFile << sourceFile.rdbuf();
sourceFile.close();
targetFile.close();
return false;
}
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)) {
LOG(LogError) << "Destination file is actually a directory:";
LOG(LogError) << _destination_path;
return true;
}
if (!_overwrite && exists(_destination_path)) {
LOG(LogError) << "Destination file exists and the overwrite flag "
"has not been set";
return true;
}
#if defined(_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.
#if defined(_WIN64)
if (_wunlink(Utils::String::stringToWideString(path).c_str()) != 0) {
LOG(LogError) << "Couldn't delete file, permission problems?";
LOG(LogError) << path;
return true;
}
else {
return false;
}
#else
if (unlink(path.c_str()) != 0) {
LOG(LogError) << "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);
#endif
2020-06-22 15:27:53 +00:00
}
bool removeDirectory(const std::string& path)
{
if (getDirContent(path).size() != 0) {
LOG(LogError) << "Couldn't delete directory as it's not empty";
LOG(LogError) << path;
return false;
}
2021-02-01 21:06:28 +00:00
if (isSymlink(path)) {
LOG(LogError) << "Couldn't delete directory as it's actually a symlink";
LOG(LogError) << path;
return false;
}
#if defined(_WIN64)
if (_wrmdir(Utils::String::stringToWideString(path).c_str()) != 0) {
#else
if (rmdir(path.c_str()) != 0) {
#endif
LOG(LogError) << "Couldn't delete directory, permission problems?";
LOG(LogError) << path;
return false;
}
return true;
}
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.
#if defined(_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;
#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.
#if defined(_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);
#endif
2020-06-22 15:27:53 +00:00
}
bool exists(const std::string& _path)
{
std::string path = getGenericPath(_path);
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
struct stat info;
return (stat(path.c_str(), &info) == 0);
#elif defined(_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);
#endif
2020-06-22 15:27:53 +00:00
}
bool driveExists(const std::string& _path)
{
#if defined(_WIN64)
std::string path = getGenericPath(_path);
// Try to add a dot or a backslash and a dot depending on how the drive
// letter was defined by the user.
if (path.length() == 2 && path.at(1) == ':')
path += "\\.";
else if (path.length() == 3 && path.at(1) == ':')
path += ".";
struct _stat64 info;
return (_wstat64(Utils::String::stringToWideString(path).c_str(), &info) == 0);
#else
return false;
#endif
}
2020-06-22 15:27:53 +00:00
bool isAbsolute(const std::string& _path)
{
std::string path = getGenericPath(_path);
#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);
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
struct stat info;
if (stat(path.c_str(), &info) != 0)
return false;
#elif defined(_WIN64)
struct stat64 info;
if (_wstat64(Utils::String::stringToWideString(path).c_str(), &info) != 0)
return false;
#else
struct stat64 info;
2020-06-22 15:27:53 +00:00
if (stat64(path.c_str(), &info) != 0)
return false;
#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);
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
struct stat info;
if (stat(path.c_str(), &info) != 0)
return false;
#elif defined(_WIN64)
struct stat64 info;
if (_wstat64(Utils::String::stringToWideString(path).c_str(), &info) != 0)
return false;
#else
struct stat64 info;
if (stat64(path.c_str(), &info) != 0)
2020-06-22 15:27:53 +00:00
return false;
#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);
#if defined(_WIN64)
2020-06-22 15:27:53 +00:00
// Check for symlink attribute.
const DWORD Attributes =
GetFileAttributesW(Utils::String::stringToWideString(path).c_str());
2020-06-22 15:27:53 +00:00
if ((Attributes != INVALID_FILE_ATTRIBUTES) &&
(Attributes & FILE_ATTRIBUTE_REPARSE_POINT))
return true;
#else
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
2020-06-22 15:27:53 +00:00
struct stat info;
if (lstat(path.c_str(), &info) != 0)
return false;
#else
struct stat64 info;
if (lstat64(path.c_str(), &info) != 0)
return false;
#endif
2020-06-22 15:27:53 +00:00
// 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);
#if defined(_WIN64)
2020-06-22 15:27:53 +00:00
// Check for hidden attribute.
const DWORD Attributes =
GetFileAttributesW(Utils::String::stringToWideString(path).c_str());
2020-06-22 15:27:53 +00:00
if ((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN))
return true;
#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::
} // Utils::