mirror of
https://github.com/Detanup01/gbe_fork.git
synced 2024-11-27 21:24:01 +08:00
563 lines
14 KiB
C++
563 lines
14 KiB
C++
|
/*
|
||
|
* Copyright (C) Nemirtingas
|
||
|
* This file is part of System.
|
||
|
*
|
||
|
* System is free software; you can redistribute it
|
||
|
* and/or modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 3 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* System is distributed in the hope that it will be
|
||
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with System; if not, see
|
||
|
* <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "Filesystem.h"
|
||
|
#include "Encoding.hpp"
|
||
|
#include "System_internals.h"
|
||
|
|
||
|
#if defined(SYSTEM_OS_WINDOWS)
|
||
|
#define WIN32_LEAN_AND_MEAN
|
||
|
#define VC_EXTRALEAN
|
||
|
#define NOMINMAX
|
||
|
#include <Windows.h>
|
||
|
|
||
|
#ifdef CreateDirectory
|
||
|
#undef CreateDirectory
|
||
|
#endif
|
||
|
#ifdef DeleteFile
|
||
|
#undef DeleteFile
|
||
|
#endif
|
||
|
|
||
|
#elif defined(SYSTEM_OS_LINUX) || defined(SYSTEM_OS_APPLE)
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/ioctl.h> // get iface broadcast
|
||
|
#include <sys/stat.h> // stats on a file (is directory, size, mtime)
|
||
|
|
||
|
#include <dirent.h> // to open directories
|
||
|
#include <dlfcn.h> // dlopen (like dll for linux)
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <limits.h> // PATH_MAX
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#else
|
||
|
#error "unknown arch"
|
||
|
#endif
|
||
|
|
||
|
#include <fstream>
|
||
|
#include <algorithm>
|
||
|
#include <iterator>
|
||
|
|
||
|
#include <ctime>
|
||
|
|
||
|
namespace System {
|
||
|
namespace Filesystem {
|
||
|
|
||
|
static void _CleanSlashes(std::string& str);
|
||
|
|
||
|
std::string Filename(std::string const& path)
|
||
|
{
|
||
|
size_t pos = path.find_last_of("/\\");
|
||
|
if (pos != std::string::npos)
|
||
|
return path.substr(pos+1);
|
||
|
|
||
|
return path;
|
||
|
}
|
||
|
|
||
|
std::string Dirname(std::string const& path)
|
||
|
{
|
||
|
std::string r(path);
|
||
|
_CleanSlashes(r);
|
||
|
size_t pos = r.find_last_of("/\\");
|
||
|
|
||
|
if (pos == std::string::npos || (pos == 0 && r.length() == 1))
|
||
|
return std::string();
|
||
|
|
||
|
if (pos == 0)
|
||
|
++pos;
|
||
|
|
||
|
return r.substr(0, pos);
|
||
|
}
|
||
|
|
||
|
std::string Join(StringView r, StringView l)
|
||
|
{
|
||
|
std::string result(r.to_string());
|
||
|
|
||
|
result += Separator;
|
||
|
result += l.to_string();
|
||
|
|
||
|
_CleanSlashes(result);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
std::string CanonicalPath(std::string const& path)
|
||
|
{
|
||
|
if (IsAbsolute(path))
|
||
|
return CleanPath(path);
|
||
|
|
||
|
return CleanPath(Join(GetCwd(),path));
|
||
|
}
|
||
|
|
||
|
size_t FileSize(std::string const& path)
|
||
|
{
|
||
|
std::ifstream in_file(path, std::ios::in | std::ios::binary | std::ios::ate);
|
||
|
if (in_file)
|
||
|
{
|
||
|
return in_file.tellg();
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::chrono::system_clock::time_point FileATime(std::string const& path)
|
||
|
{
|
||
|
struct stat file_stat = {};
|
||
|
if (stat(path.c_str(), &file_stat) != 0)
|
||
|
return std::chrono::system_clock::time_point{};
|
||
|
|
||
|
return std::chrono::system_clock::from_time_t(file_stat.st_atime);
|
||
|
}
|
||
|
|
||
|
std::chrono::system_clock::time_point FileMTime(std::string const& path)
|
||
|
{
|
||
|
struct stat file_stat = {};
|
||
|
if (stat(path.c_str(), &file_stat) != 0)
|
||
|
return std::chrono::system_clock::time_point{};
|
||
|
|
||
|
return std::chrono::system_clock::from_time_t(file_stat.st_mtime);
|
||
|
}
|
||
|
|
||
|
std::chrono::system_clock::time_point FileCTime(std::string const& path)
|
||
|
{
|
||
|
struct stat file_stat = {};
|
||
|
if (stat(path.c_str(), &file_stat) != 0)
|
||
|
return std::chrono::system_clock::time_point{};
|
||
|
|
||
|
return std::chrono::system_clock::from_time_t(file_stat.st_ctime);
|
||
|
}
|
||
|
|
||
|
#ifdef SYSTEM_OS_WINDOWS
|
||
|
|
||
|
static void _CleanSlashes(std::string& str)
|
||
|
{
|
||
|
size_t pos;
|
||
|
std::replace(str.begin(), str.end(), '/', '\\');
|
||
|
|
||
|
while ((pos = str.find("\\\\")) != std::string::npos)
|
||
|
str.replace(pos, 2, "\\");
|
||
|
|
||
|
pos = 0;
|
||
|
while ((pos = str.find("\\.", pos)) != std::string::npos)
|
||
|
{
|
||
|
if (str[pos + 2] == '\\' || (pos + 2) >= str.length())
|
||
|
{
|
||
|
str.replace(pos, 3, "\\");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
++pos;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::string GetCwd()
|
||
|
{
|
||
|
DWORD size = GetCurrentDirectoryW(0, nullptr);
|
||
|
if (size == 0)
|
||
|
return ".";
|
||
|
|
||
|
std::wstring wdirectory;
|
||
|
++size;
|
||
|
wdirectory.resize(size);
|
||
|
wdirectory.resize(GetCurrentDirectoryW(size, &wdirectory[0]));
|
||
|
wdirectory += L'\\';
|
||
|
|
||
|
return System::Encoding::WCharToUtf8(wdirectory);
|
||
|
}
|
||
|
|
||
|
bool IsAbsolute(std::string const& path)
|
||
|
{
|
||
|
return path.length() >= 2 && (((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z')) && path[1] == ':');
|
||
|
}
|
||
|
|
||
|
std::string CleanPath(std::string const& path)
|
||
|
{
|
||
|
std::string cleaned_path(path);
|
||
|
size_t pos;
|
||
|
size_t size;
|
||
|
|
||
|
_CleanSlashes(cleaned_path);
|
||
|
|
||
|
pos = 0;
|
||
|
while ((pos = cleaned_path.find("\\..", pos)) != std::string::npos )
|
||
|
{
|
||
|
if (cleaned_path[pos + 3] == '\\' || (pos+3) >= cleaned_path.length())
|
||
|
{
|
||
|
if (pos == 0)
|
||
|
size = 3;
|
||
|
else
|
||
|
{
|
||
|
size_t parent_pos = cleaned_path.rfind("\\", pos - 1);
|
||
|
if (parent_pos == std::string::npos)
|
||
|
{
|
||
|
size = pos + 3;
|
||
|
pos = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
size = 3 + pos - parent_pos;
|
||
|
pos = parent_pos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cleaned_path.replace(pos, size, "");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
++pos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return cleaned_path;
|
||
|
}
|
||
|
|
||
|
bool IsDir(std::string const& path)
|
||
|
{
|
||
|
std::wstring wpath(System::Encoding::Utf8ToWChar(path));
|
||
|
|
||
|
DWORD attrs = GetFileAttributesW(wpath.c_str());
|
||
|
return attrs != INVALID_FILE_ATTRIBUTES && attrs & FILE_ATTRIBUTE_DIRECTORY;
|
||
|
}
|
||
|
|
||
|
bool IsFile(std::string const& path)
|
||
|
{
|
||
|
std::wstring wpath(System::Encoding::Utf8ToWChar(path));
|
||
|
|
||
|
DWORD attrs = GetFileAttributesW(wpath.c_str());
|
||
|
return attrs != INVALID_FILE_ATTRIBUTES && ((attrs & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY);
|
||
|
}
|
||
|
|
||
|
bool Exists(std::string const& path)
|
||
|
{
|
||
|
std::wstring wpath(System::Encoding::Utf8ToWChar(path));
|
||
|
|
||
|
DWORD attrs = GetFileAttributesW(wpath.c_str());
|
||
|
return attrs != INVALID_FILE_ATTRIBUTES;
|
||
|
}
|
||
|
|
||
|
bool CreateDirectory(std::string const& directory, bool recursive)
|
||
|
{
|
||
|
size_t pos = 0;
|
||
|
struct _stat sb;
|
||
|
|
||
|
std::wstring sub_dir;
|
||
|
std::wstring wdirectory(System::Encoding::Utf8ToWChar(directory));
|
||
|
|
||
|
if (wdirectory.empty())
|
||
|
return false;
|
||
|
|
||
|
if (recursive)
|
||
|
{
|
||
|
pos = 3;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
pos = wdirectory.find_first_of(L"\\/", pos + 1);
|
||
|
sub_dir = std::move(wdirectory.substr(0, pos));
|
||
|
if (_wstat(sub_dir.c_str(), &sb) == 0)
|
||
|
{
|
||
|
if (!(sb.st_mode & _S_IFDIR))
|
||
|
{// A subpath in the target is not a directory
|
||
|
return false;
|
||
|
}
|
||
|
// Folder Exists
|
||
|
}
|
||
|
else if (CreateDirectoryW(wdirectory.substr(0, pos).c_str(), NULL) == FALSE && GetLastError() != ERROR_ALREADY_EXISTS)
|
||
|
{// Failed to create directory
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
while (pos != std::string::npos);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return (CreateDirectoryW(wdirectory.c_str(), NULL) != FALSE || GetLastError() == ERROR_ALREADY_EXISTS);
|
||
|
}
|
||
|
|
||
|
bool DeleteFile(std::string const& path)
|
||
|
{
|
||
|
std::wstring wpath(System::Encoding::Utf8ToWChar(path));
|
||
|
return DeleteFileW(wpath.c_str()) == TRUE || GetLastError() == ERROR_FILE_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
static std::vector<std::wstring> ListFiles(std::wstring const& path, bool files_only, bool recursive)
|
||
|
{
|
||
|
std::vector<std::wstring> files;
|
||
|
WIN32_FIND_DATAW hfind_data;
|
||
|
HANDLE hfind = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
std::wstring search_path = path;
|
||
|
|
||
|
if (*path.rbegin() != L'\\')
|
||
|
search_path += L'\\';
|
||
|
|
||
|
search_path += L'*';
|
||
|
|
||
|
// Start iterating over the files in the path directory.
|
||
|
hfind = FindFirstFileW(search_path.c_str(), &hfind_data);
|
||
|
if (hfind != INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
search_path.pop_back();
|
||
|
do // Managed to locate and create an handle to that folder.
|
||
|
{
|
||
|
if (wcscmp(L".", hfind_data.cFileName) == 0
|
||
|
|| wcscmp(L"..", hfind_data.cFileName) == 0)
|
||
|
continue;
|
||
|
|
||
|
if (hfind_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||
|
{
|
||
|
if (recursive)
|
||
|
{
|
||
|
std::wstring dir_name = hfind_data.cFileName;
|
||
|
|
||
|
std::vector<std::wstring> sub_files = std::move(ListFiles(search_path + dir_name, files_only, true));
|
||
|
std::transform(sub_files.begin(), sub_files.end(), std::back_inserter(files), [&dir_name](std::wstring& Filename)
|
||
|
{
|
||
|
return dir_name + L'\\' + Filename;
|
||
|
});
|
||
|
}
|
||
|
if (!files_only)
|
||
|
{
|
||
|
files.emplace_back(hfind_data.cFileName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
files.emplace_back(hfind_data.cFileName);
|
||
|
}
|
||
|
} while (FindNextFileW(hfind, &hfind_data) == TRUE);
|
||
|
FindClose(hfind);
|
||
|
}
|
||
|
|
||
|
return files;
|
||
|
}
|
||
|
|
||
|
std::vector<std::string> ListFiles(std::string const& path, bool files_only, bool recursive)
|
||
|
{
|
||
|
std::vector<std::string> files;
|
||
|
std::wstring wpath(System::Encoding::Utf8ToWChar(path));
|
||
|
|
||
|
std::vector<std::wstring> wfiles(std::move(ListFiles(wpath, files_only, recursive)));
|
||
|
|
||
|
files.reserve(wfiles.size());
|
||
|
std::transform(wfiles.begin(), wfiles.end(), std::back_inserter(files), [](std::wstring const& wFilename)
|
||
|
{
|
||
|
return System::Encoding::WCharToUtf8(wFilename);
|
||
|
});
|
||
|
|
||
|
return files;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
static void _CleanSlashes(std::string& str)
|
||
|
{
|
||
|
size_t pos;
|
||
|
std::replace(str.begin(), str.end(), '\\', '/');
|
||
|
|
||
|
while ((pos = str.find("//")) != std::string::npos)
|
||
|
str.replace(pos, 2, "/");
|
||
|
|
||
|
pos = 0;
|
||
|
while ((pos = str.find("/.", pos)) != std::string::npos)
|
||
|
{
|
||
|
if (str[pos + 2] == '/' || (pos + 2) >= str.length())
|
||
|
{
|
||
|
str.replace(pos, 3, "/");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
++pos;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::string GetCwd()
|
||
|
{
|
||
|
char buff[4096];
|
||
|
std::string tmp(getcwd(buff, 4096) == nullptr ? "." : buff);
|
||
|
|
||
|
tmp += '/';
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
bool IsAbsolute(std::string const& path)
|
||
|
{
|
||
|
return path[0] == '/';
|
||
|
}
|
||
|
|
||
|
std::string CleanPath(std::string const& path)
|
||
|
{
|
||
|
std::string cleaned_path(path);
|
||
|
size_t pos;
|
||
|
size_t size;
|
||
|
|
||
|
std::replace(cleaned_path.begin(), cleaned_path.end(), '\\', '/');
|
||
|
|
||
|
_CleanSlashes(cleaned_path);
|
||
|
|
||
|
pos = 0;
|
||
|
while ((pos = cleaned_path.find("/..", pos)) != std::string::npos)
|
||
|
{
|
||
|
if (cleaned_path[pos + 3] == '/' || (pos + 3) >= cleaned_path.length())
|
||
|
{
|
||
|
if (pos == 0)
|
||
|
size = 3;
|
||
|
else
|
||
|
{
|
||
|
size_t parent_pos = cleaned_path.rfind("/", pos - 1);
|
||
|
if (parent_pos == std::string::npos)
|
||
|
{
|
||
|
size = pos + 3;
|
||
|
pos = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
size = 3 + pos - parent_pos;
|
||
|
pos = parent_pos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cleaned_path.replace(pos, size, "");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
++pos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return cleaned_path;
|
||
|
}
|
||
|
|
||
|
bool IsDir(std::string const& path)
|
||
|
{
|
||
|
struct stat sb;
|
||
|
if (stat(path.c_str(), &sb) == 0)
|
||
|
{
|
||
|
return S_ISDIR(sb.st_mode);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool IsFile(std::string const& path)
|
||
|
{
|
||
|
struct stat sb;
|
||
|
if (stat(path.c_str(), &sb) == 0)
|
||
|
{
|
||
|
return S_ISREG(sb.st_mode);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool Exists(std::string const& path)
|
||
|
{
|
||
|
struct stat sb;
|
||
|
return stat(path.c_str(), &sb) == 0;
|
||
|
}
|
||
|
|
||
|
bool CreateDirectory(std::string const& directory, bool recursive)
|
||
|
{
|
||
|
size_t pos = 0;
|
||
|
struct stat sb;
|
||
|
|
||
|
std::string sub_dir;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
pos = directory.find("/", pos + 1);
|
||
|
sub_dir = std::move(directory.substr(0, pos));
|
||
|
if (stat(sub_dir.c_str(), &sb) == 0)
|
||
|
{
|
||
|
if (!S_ISDIR(sb.st_mode))
|
||
|
{// A subpath in the target is not a directory
|
||
|
return false;
|
||
|
}
|
||
|
// Folder Exists
|
||
|
}
|
||
|
else if (mkdir(sub_dir.c_str(), 0755) < 0 && errno != EEXIST)
|
||
|
{// Failed to create directory (no permission?)
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
while (pos != std::string::npos);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool DeleteFile(std::string const& path)
|
||
|
{
|
||
|
return unlink(path.c_str()) == 0;
|
||
|
}
|
||
|
|
||
|
std::vector<std::string> ListFiles(std::string const& path, bool files_only, bool recursive)
|
||
|
{
|
||
|
std::vector<std::string> files;
|
||
|
|
||
|
std::string search_path = path;
|
||
|
|
||
|
if (*path.rbegin() != Separator)
|
||
|
search_path += Separator;
|
||
|
|
||
|
DIR* dir = opendir(search_path.c_str());
|
||
|
struct dirent* entry;
|
||
|
|
||
|
if (dir == nullptr)
|
||
|
return files;
|
||
|
|
||
|
while ((entry = readdir(dir)) != nullptr)
|
||
|
{
|
||
|
if (strcmp(entry->d_name, ".") == 0
|
||
|
|| strcmp(entry->d_name, "..") == 0)
|
||
|
continue;
|
||
|
|
||
|
if (entry->d_type == DT_DIR)
|
||
|
{
|
||
|
if (recursive)
|
||
|
{
|
||
|
std::string dir_name = entry->d_name;
|
||
|
std::vector<std::string> sub_files = std::move(ListFiles(search_path + dir_name, true));
|
||
|
std::transform(sub_files.begin(), sub_files.end(), std::back_inserter(files), [&dir_name](std::string& Filename)
|
||
|
{
|
||
|
return dir_name + Separator + Filename;
|
||
|
});
|
||
|
}
|
||
|
if (!files_only)
|
||
|
{
|
||
|
files.emplace_back(entry->d_name);
|
||
|
}
|
||
|
}
|
||
|
else if (entry->d_type == DT_REG)
|
||
|
{
|
||
|
files.emplace_back(entry->d_name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
closedir(dir);
|
||
|
|
||
|
return files;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
}
|