|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma once |
|
|
|
|
|
#include <fstream> |
|
|
#include <cctype> |
|
|
|
|
|
#if defined(ENV_HAS_STD_FILESYSTEM) |
|
|
#include <filesystem> |
|
|
#endif |
|
|
#if defined(ENV_HAS_POSIX_FILE_STAT) |
|
|
#include <sys/types.h> |
|
|
#include <sys/stat.h> |
|
|
#include <dirent.h> |
|
|
#include <cstring> |
|
|
#include "utilities/scalar_guard.hpp" |
|
|
#endif |
|
|
#if defined(ENV_HAS_WIN_API) |
|
|
#include <Windows.h> |
|
|
#endif |
|
|
|
|
|
#include <string> |
|
|
|
|
|
namespace utilities { |
|
|
constexpr auto error_size = static_cast<uintmax_t>(-1); |
|
|
|
|
|
enum class file_type { |
|
|
none, |
|
|
not_found, |
|
|
regular, |
|
|
directory, |
|
|
symlink, |
|
|
block, |
|
|
character, |
|
|
fifo, |
|
|
socket, |
|
|
unknown, |
|
|
}; |
|
|
|
|
|
inline bool exists(const std::string& path) { |
|
|
#if defined(ENV_HAS_STD_FILESYSTEM) |
|
|
return std::filesystem::exists(path); |
|
|
#elif defined(ENV_HAS_POSIX_FILE_STAT) |
|
|
struct ::stat path_stat{}; |
|
|
return 0 == ::stat(path.c_str(), &path_stat); |
|
|
#else |
|
|
#error "Unsupported platform, native file system API is required." |
|
|
#endif |
|
|
} |
|
|
|
|
|
inline uintmax_t file_size(const std::string& path) { |
|
|
#if defined(ENV_HAS_STD_FILESYSTEM) |
|
|
return std::filesystem::file_size(path); |
|
|
#elif defined(ENV_HAS_POSIX_FILE_STAT) |
|
|
struct ::stat path_stat{}; |
|
|
if (0 == ::stat(path.c_str(), &path_stat)) { |
|
|
return path_stat.st_size; |
|
|
} |
|
|
return error_size; |
|
|
#else |
|
|
#error "Unsupported platform, native file system API is required." |
|
|
#endif |
|
|
} |
|
|
|
|
|
inline file_type status(const std::string& path) { |
|
|
#if defined(ENV_HAS_STD_FILESYSTEM) |
|
|
if (!exists(path)) { |
|
|
return file_type::not_found; |
|
|
} |
|
|
|
|
|
const auto status = std::filesystem::status(path); |
|
|
if (std::filesystem::is_regular_file(status)) { |
|
|
return file_type::regular; |
|
|
} |
|
|
if (std::filesystem::is_directory(status)) { |
|
|
return file_type::directory; |
|
|
} |
|
|
if (std::filesystem::is_symlink(status)) { |
|
|
return file_type::symlink; |
|
|
} |
|
|
if (std::filesystem::is_block_file(status)) { |
|
|
return file_type::block; |
|
|
} |
|
|
if (std::filesystem::is_character_file(status)) { |
|
|
return file_type::character; |
|
|
} |
|
|
if (std::filesystem::is_fifo(status)) { |
|
|
return file_type::fifo; |
|
|
} |
|
|
if (std::filesystem::is_socket(status)) { |
|
|
return file_type::socket; |
|
|
} |
|
|
return file_type::unknown; |
|
|
#elif defined(ENV_HAS_POSIX_FILE_STAT) |
|
|
struct ::stat path_stat{}; |
|
|
if (0 != ::stat(path.c_str(), &path_stat)) { |
|
|
return file_type::not_found; |
|
|
} |
|
|
|
|
|
if (S_ISREG(path_stat.st_mode)) { |
|
|
return file_type::regular; |
|
|
} |
|
|
if (S_ISDIR(path_stat.st_mode)) { |
|
|
return file_type::directory; |
|
|
} |
|
|
if (S_ISLNK(path_stat.st_mode)) { |
|
|
return file_type::symlink; |
|
|
} |
|
|
if (S_ISBLK(path_stat.st_mode)) { |
|
|
return file_type::block; |
|
|
} |
|
|
if (S_ISCHR(path_stat.st_mode)) { |
|
|
return file_type::character; |
|
|
} |
|
|
if (S_ISFIFO(path_stat.st_mode)) { |
|
|
return file_type::fifo; |
|
|
} |
|
|
if (S_ISSOCK(path_stat.st_mode)) { |
|
|
return file_type::socket; |
|
|
} |
|
|
return file_type::unknown; |
|
|
#else |
|
|
#error "Unsupported platform, native file system API is required." |
|
|
#endif |
|
|
} |
|
|
|
|
|
inline bool is_regular_file(const std::string& path) { |
|
|
#if defined(ENV_HAS_STD_FILESYSTEM) |
|
|
return std::filesystem::is_regular_file(path); |
|
|
#elif defined(ENV_HAS_POSIX_FILE_STAT) |
|
|
struct ::stat path_stat{}; |
|
|
return 0 == ::stat(path.c_str(), &path_stat) && S_ISREG(path_stat.st_mode); |
|
|
#else |
|
|
#error "Unsupported platform, native file system API is required." |
|
|
#endif |
|
|
} |
|
|
|
|
|
inline bool is_directory(const std::string& path) { |
|
|
#if defined(ENV_HAS_STD_FILESYSTEM) |
|
|
return std::filesystem::is_directory(path); |
|
|
#elif defined(ENV_HAS_POSIX_FILE_STAT) |
|
|
struct ::stat path_stat{}; |
|
|
return 0 == ::stat(path.c_str(), &path_stat) && S_ISDIR(path_stat.st_mode); |
|
|
#else |
|
|
#error "Unsupported platform, native file system API is required." |
|
|
#endif |
|
|
} |
|
|
|
|
|
inline bool is_empty(const std::string& path) { |
|
|
#if defined(ENV_HAS_STD_FILESYSTEM) |
|
|
return std::filesystem::is_empty(path); |
|
|
#elif defined(ENV_HAS_POSIX_FILE_STAT) |
|
|
if (!exists(path)) |
|
|
return false; |
|
|
|
|
|
if (is_regular_file(path)) |
|
|
return 0 == file_size(path); |
|
|
if (is_directory(path)) { |
|
|
auto dir = scalar_guard<DIR*>(::opendir(path.c_str()), ::closedir); |
|
|
if (nullptr == dir.get()) { |
|
|
return false; |
|
|
} |
|
|
|
|
|
struct ::dirent* entry; |
|
|
while ((entry = ::readdir(dir.get())) != nullptr) { |
|
|
if (0 != ::strcmp(entry->d_name, ".") && 0 != ::strcmp(entry->d_name, "..")) { |
|
|
return false; |
|
|
} |
|
|
} |
|
|
return true; |
|
|
} |
|
|
return false; |
|
|
#else |
|
|
#error "Unsupported platform, native file system API is required." |
|
|
#endif |
|
|
} |
|
|
|
|
|
inline bool read(const std::string& file, void* data, const uintmax_t& size) { |
|
|
std::ifstream stream; |
|
|
stream.open(file, std::ios_base::binary | std::ios_base::in); |
|
|
if (stream.is_open()) { |
|
|
stream.read(static_cast<char*>(data), static_cast<std::streamsize>(size)); |
|
|
stream.close(); |
|
|
return true; |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
inline bool write(const std::string& file, const void* data, const uintmax_t& size) { |
|
|
std::ofstream stream; |
|
|
stream.open(file, std::ios_base::binary | std::ios_base::out); |
|
|
if (stream.is_open()) { |
|
|
stream.write(static_cast<const char*>(data), static_cast<std::streamsize>(size)); |
|
|
stream.close(); |
|
|
return true; |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
inline bool create_directory(const std::string& path) { |
|
|
#if defined(ENV_HAS_STD_FILESYSTEM) |
|
|
return std::filesystem::create_directories(path); |
|
|
#elif defined(ENV_HAS_POSIX_FILE_STAT) |
|
|
return 0 == ::mkdir(path.c_str(), 0755); |
|
|
#else |
|
|
#error "Unsupported platform, native file system API is required." |
|
|
#endif |
|
|
} |
|
|
|
|
|
inline std::string get_file_name(const std::string& file) { |
|
|
#if defined(ENV_HAS_STD_FILESYSTEM) |
|
|
return std::filesystem::path(file).filename().string(); |
|
|
#else |
|
|
if (const auto pos = file.find_last_of("/\\"); std::string::npos != pos) { |
|
|
return file.substr(pos + 1); |
|
|
} |
|
|
return file; |
|
|
#endif |
|
|
} |
|
|
|
|
|
inline std::string get_file_extension(const std::string& file) { |
|
|
#if defined(ENV_HAS_STD_FILESYSTEM) |
|
|
return std::filesystem::path(file).extension().string(); |
|
|
#else |
|
|
if (const auto pos = file.find_last_of('.'); std::string::npos != pos) { |
|
|
return file.substr(pos + 1); |
|
|
} |
|
|
return ""; |
|
|
#endif |
|
|
} |
|
|
|
|
|
inline std::string get_legal_name(const std::string& name) { |
|
|
std::string temp = name; |
|
|
|
|
|
size_t pos = temp.find_last_of("/\\"); |
|
|
if (pos != std::string::npos) pos = 0; |
|
|
|
|
|
for (size_t i = pos; i < temp.size(); ++i) |
|
|
if (!(::isalnum(temp[i]) || '-' == temp[i] || '_' == temp[i])) temp[i] = '_'; |
|
|
return temp; |
|
|
} |
|
|
|
|
|
} |
|
|
|