| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #include <FCConfig.h>
|
| |
|
| | #include <algorithm>
|
| | #include <codecvt>
|
| | #include <cstring>
|
| | #include <fstream>
|
| | #include <iostream>
|
| | #include <system_error>
|
| | #ifdef FC_OS_WIN32
|
| | # include <Windows.h>
|
| | #else
|
| | # include <unistd.h>
|
| | #endif
|
| |
|
| | #include "FileInfo.h"
|
| | #include "Exception.h"
|
| | #include "TimeInfo.h"
|
| |
|
| | using namespace Base;
|
| | namespace fs = std::filesystem;
|
| |
|
| |
|
| |
|
| |
|
| | #ifdef FC_OS_WIN32
|
| | std::string ConvertFromWideString(const std::wstring& string)
|
| | {
|
| | int neededSize = WideCharToMultiByte(CP_UTF8, 0, string.c_str(), -1, 0, 0, 0, 0);
|
| | char* CharString = new char[static_cast<size_t>(neededSize)];
|
| | WideCharToMultiByte(CP_UTF8, 0, string.c_str(), -1, CharString, neededSize, 0, 0);
|
| | std::string String(CharString);
|
| | delete[] CharString;
|
| | CharString = NULL;
|
| | return String;
|
| | }
|
| |
|
| | std::wstring ConvertToWideString(const std::string& string)
|
| | {
|
| | int neededSize = MultiByteToWideChar(CP_UTF8, 0, string.c_str(), -1, 0, 0);
|
| | wchar_t* wideCharString = new wchar_t[static_cast<size_t>(neededSize)];
|
| | MultiByteToWideChar(CP_UTF8, 0, string.c_str(), -1, wideCharString, neededSize);
|
| | std::wstring wideString(wideCharString);
|
| | delete[] wideCharString;
|
| | wideCharString = NULL;
|
| | return wideString;
|
| | }
|
| | #endif
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | FileInfo::FileInfo(const char* fileName)
|
| | {
|
| | setFile(fileName);
|
| | }
|
| |
|
| | FileInfo::FileInfo(const std::string& fileName)
|
| | {
|
| | setFile(fileName.c_str());
|
| | }
|
| |
|
| | const std::string& FileInfo::getTempPath()
|
| | {
|
| | static std::string tempPath;
|
| |
|
| | if (tempPath.empty()) {
|
| | fs::path tmp = fs::temp_directory_path();
|
| | tmp += fs::path::preferred_separator;
|
| | tempPath = pathToString(tmp);
|
| | }
|
| |
|
| | return tempPath;
|
| | }
|
| |
|
| | std::string FileInfo::getTempFileName(const char* FileName, const char* Path)
|
| | {
|
| |
|
| |
|
| | #ifdef FC_OS_WIN32
|
| | wchar_t buf[MAX_PATH + 2];
|
| |
|
| |
|
| | std::wstring path;
|
| | if (Path) {
|
| | path = ConvertToWideString(std::string(Path));
|
| | }
|
| | else {
|
| | path = ConvertToWideString(getTempPath());
|
| | }
|
| |
|
| |
|
| | std::wstring file;
|
| | if (FileName) {
|
| | file = ConvertToWideString(std::string(FileName));
|
| | }
|
| | else {
|
| | file = L"FCTempFile";
|
| | }
|
| |
|
| |
|
| |
|
| | GetTempFileNameW(path.c_str(), file.c_str(), 0, buf);
|
| | DeleteFileW(buf);
|
| |
|
| | return std::string(ConvertFromWideString(std::wstring(buf)));
|
| | #else
|
| | std::string buf;
|
| |
|
| |
|
| | if (Path) {
|
| | buf = Path;
|
| | }
|
| | else {
|
| | buf = getTempPath();
|
| | }
|
| |
|
| |
|
| | if (FileName) {
|
| | buf += "/";
|
| | buf += FileName;
|
| | buf += "XXXXXX";
|
| | }
|
| | else {
|
| | buf += "/fileXXXXXX";
|
| | }
|
| |
|
| | std::vector<char> vec;
|
| | std::copy(buf.begin(), buf.end(), std::back_inserter(vec));
|
| | vec.push_back('\0');
|
| |
|
| |
|
| | int id = mkstemp(vec.data());
|
| | if (id > -1) {
|
| | FILE* file = fdopen(id, "w");
|
| | fclose(file);
|
| | vec.pop_back();
|
| | std::string str(vec.begin(), vec.end());
|
| | buf.swap(str);
|
| | unlink(buf.c_str());
|
| | }
|
| | return buf;
|
| | #endif
|
| | }
|
| |
|
| | fs::path FileInfo::stringToPath(const std::string& str)
|
| | {
|
| | #ifdef FC_OS_WIN32
|
| | std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
| | fs::path path(converter.from_bytes(str));
|
| | #else
|
| | fs::path path(str);
|
| | #endif
|
| | return path;
|
| | }
|
| |
|
| | std::string FileInfo::pathToString(const fs::path& path)
|
| | {
|
| | #ifdef FC_OS_WIN32
|
| | std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
| | return converter.to_bytes(path.wstring());
|
| | #else
|
| | return path.string();
|
| | #endif
|
| | }
|
| |
|
| | void FileInfo::setFile(const char* name)
|
| | {
|
| | if (!name) {
|
| | FileName.clear();
|
| | return;
|
| | }
|
| |
|
| | FileName = name;
|
| |
|
| |
|
| | if (FileName.substr(0, 2) == std::string("\\\\")) {
|
| | std::replace(FileName.begin() + 2, FileName.end(), '\\', '/');
|
| | }
|
| | else {
|
| | std::replace(FileName.begin(), FileName.end(), '\\', '/');
|
| | }
|
| | }
|
| |
|
| | std::string FileInfo::filePath() const
|
| | {
|
| | return FileName;
|
| | }
|
| |
|
| | std::string FileInfo::fileName() const
|
| | {
|
| | return FileName.substr(FileName.find_last_of('/') + 1);
|
| | }
|
| |
|
| | std::string FileInfo::dirPath() const
|
| | {
|
| | std::size_t last_pos {};
|
| | std::string retval;
|
| | last_pos = FileName.find_last_of('/');
|
| | if (last_pos != std::string::npos) {
|
| | retval = FileName.substr(0, last_pos);
|
| | }
|
| | else {
|
| | retval = pathToString(fs::current_path());
|
| | }
|
| | return retval;
|
| | }
|
| |
|
| | std::string FileInfo::fileNamePure() const
|
| | {
|
| | std::string temp = fileName();
|
| | std::string::size_type pos = temp.find_last_of('.');
|
| |
|
| | if (pos != std::string::npos) {
|
| | return temp.substr(0, pos);
|
| | }
|
| |
|
| | return temp;
|
| | }
|
| |
|
| | std::wstring FileInfo::toStdWString() const
|
| | {
|
| |
|
| |
|
| | #ifdef FC_OS_WIN32
|
| | return ConvertToWideString(FileName);
|
| | #else
|
| |
|
| | throw Base::FileException("Cannot use FileInfo::toStdWString() on this platform");
|
| | #endif
|
| | }
|
| |
|
| | std::string FileInfo::extension() const
|
| | {
|
| | std::string::size_type pos = FileName.find_last_of('.');
|
| | if (pos == std::string::npos) {
|
| | return {};
|
| | }
|
| | return FileName.substr(pos + 1);
|
| | }
|
| |
|
| | std::string FileInfo::completeExtension() const
|
| | {
|
| | std::string::size_type pos = FileName.find_first_of('.');
|
| | if (pos == std::string::npos) {
|
| | return {};
|
| | }
|
| | return FileName.substr(pos + 1);
|
| | }
|
| |
|
| | bool FileInfo::hasExtension(const char* Ext) const
|
| | {
|
| | #ifdef FC_OS_WIN32
|
| | return _stricmp(Ext, extension().c_str()) == 0;
|
| | #else
|
| | return strcasecmp(Ext, extension().c_str()) == 0;
|
| | #endif
|
| | }
|
| |
|
| | bool FileInfo::hasExtension(std::initializer_list<const char*> Exts) const
|
| | {
|
| | return std::ranges::any_of(Exts, [this](const char* ext) { return hasExtension(ext); });
|
| | }
|
| |
|
| | bool FileInfo::exists() const
|
| | {
|
| | fs::path path(stringToPath(FileName));
|
| | return fs::exists(path);
|
| | }
|
| |
|
| | bool FileInfo::isReadable() const
|
| | {
|
| | fs::path path = stringToPath(FileName);
|
| | if (!fs::exists(path)) {
|
| | return false;
|
| | }
|
| | fs::file_status stat = fs::status(path);
|
| | fs::perms perms = stat.permissions();
|
| | return (perms & fs::perms::owner_read) == fs::perms::owner_read;
|
| | }
|
| |
|
| | bool directoryIsWritable(const fs::path& dir)
|
| | {
|
| | try {
|
| | if (!fs::exists(dir) || !fs::is_directory(dir)) {
|
| | return false;
|
| | }
|
| | fs::path test_file = dir / (".fs_perm_test_" + std::to_string(std::rand()) + ".tmp");
|
| | {
|
| | std::ofstream ofs(test_file);
|
| | if (!ofs) {
|
| | return false;
|
| | }
|
| | }
|
| | std::error_code ec;
|
| | fs::remove(test_file, ec);
|
| | return true;
|
| | }
|
| | catch (...) {
|
| | return false;
|
| | }
|
| | }
|
| |
|
| | bool FileInfo::isWritable() const
|
| | {
|
| | fs::path path = stringToPath(FileName);
|
| | if (!fs::exists(path)) {
|
| | return false;
|
| | }
|
| | if (fs::is_directory(path)) {
|
| | return directoryIsWritable(path);
|
| | }
|
| | #ifdef FC_OS_WIN32
|
| |
|
| | std::wstring fileNameWstring = toStdWString();
|
| |
|
| | DWORD attributes = GetFileAttributes(fileNameWstring.c_str());
|
| | if (attributes == INVALID_FILE_ATTRIBUTES) {
|
| |
|
| | std::clog << "GetFileAttributes failed for file: " << FileName << '\n';
|
| |
|
| | return false;
|
| | }
|
| | if ((attributes & FILE_ATTRIBUTE_READONLY) != 0) {
|
| | return false;
|
| | }
|
| |
|
| |
|
| | HANDLE hFile = CreateFileW(
|
| | fileNameWstring.c_str(),
|
| | GENERIC_WRITE,
|
| | 0,
|
| | nullptr,
|
| | OPEN_EXISTING,
|
| | FILE_ATTRIBUTE_NORMAL,
|
| | nullptr
|
| | );
|
| |
|
| | if (hFile == INVALID_HANDLE_VALUE) {
|
| | DWORD err = GetLastError();
|
| | if (err == ERROR_SHARING_VIOLATION || err == ERROR_LOCK_VIOLATION) {
|
| | return false;
|
| | }
|
| | return false;
|
| | }
|
| | if (!CloseHandle(hFile)) {
|
| | std::clog << "CloseHandle failed for file: " << FileName
|
| | << " while checking for write access." << '\n';
|
| | }
|
| | #endif
|
| | fs::file_status stat = fs::status(path);
|
| | fs::perms perms = stat.permissions();
|
| | return (perms & fs::perms::owner_write) == fs::perms::owner_write;
|
| | }
|
| |
|
| | bool FileInfo::setPermissions(Permissions perms)
|
| | {
|
| | fs::perms mode = fs::perms::none;
|
| |
|
| | if (perms & FileInfo::ReadOnly) {
|
| | mode |= fs::perms::owner_read;
|
| | }
|
| | if (perms & FileInfo::WriteOnly) {
|
| | mode |= fs::perms::owner_write;
|
| | }
|
| |
|
| | if (mode == fs::perms::none) {
|
| | return false;
|
| | }
|
| |
|
| | fs::path file_path = stringToPath(FileName);
|
| | if (!fs::exists(file_path)) {
|
| | return false;
|
| | }
|
| |
|
| | fs::permissions(file_path, mode);
|
| | fs::file_status stat = fs::status(file_path);
|
| | return stat.permissions() == mode;
|
| | }
|
| |
|
| | bool FileInfo::isFile() const
|
| | {
|
| | fs::path path = stringToPath(FileName);
|
| | if (fs::exists(path)) {
|
| | return fs::is_regular_file(path);
|
| | }
|
| |
|
| |
|
| | return true;
|
| | }
|
| |
|
| | bool FileInfo::isDir() const
|
| | {
|
| | fs::path path = stringToPath(FileName);
|
| | if (fs::exists(path)) {
|
| | return fs::is_directory(path);
|
| | }
|
| |
|
| | return false;
|
| | }
|
| |
|
| | unsigned int FileInfo::size() const
|
| | {
|
| | unsigned int bytes {};
|
| | fs::path path = stringToPath(FileName);
|
| | if (fs::exists(path)) {
|
| | bytes = fs::file_size(path);
|
| | }
|
| |
|
| | return bytes;
|
| | }
|
| |
|
| | template<typename TP>
|
| | std::time_t to_time_t(TP tp)
|
| | {
|
| | using namespace std::chrono;
|
| | auto sctp = time_point_cast<system_clock::duration>(tp - TP::clock::now() + system_clock::now());
|
| | return system_clock::to_time_t(sctp);
|
| | }
|
| |
|
| | TimeInfo FileInfo::lastModified() const
|
| | {
|
| | TimeInfo ti = TimeInfo::null();
|
| |
|
| | if (exists()) {
|
| | fs::path path = stringToPath(FileName);
|
| | ti.setTime_t(to_time_t(fs::last_write_time(path)));
|
| | }
|
| |
|
| | return ti;
|
| | }
|
| |
|
| | bool FileInfo::deleteFile() const
|
| | {
|
| | try {
|
| | fs::path path = stringToPath(FileName);
|
| | return fs::remove(path);
|
| | }
|
| | catch (const fs::filesystem_error& e) {
|
| | std::clog << e.what() << '\n';
|
| | return false;
|
| | }
|
| | }
|
| |
|
| | bool FileInfo::renameFile(const char* NewName)
|
| | {
|
| | try {
|
| | fs::path old_path = stringToPath(FileName);
|
| | fs::path new_path = stringToPath(NewName);
|
| | fs::rename(old_path, new_path);
|
| | FileName = NewName;
|
| | return true;
|
| | }
|
| | catch (const fs::filesystem_error& e) {
|
| | std::clog << "Error in renameFile: " << e.what() << '\n';
|
| | return false;
|
| | }
|
| | }
|
| |
|
| | bool FileInfo::copyTo(const char* NewName) const
|
| | {
|
| | try {
|
| | fs::path old_path = stringToPath(FileName);
|
| | fs::path new_path = stringToPath(NewName);
|
| | fs::copy(old_path, new_path);
|
| | return true;
|
| | }
|
| | catch (const fs::filesystem_error&) {
|
| | return false;
|
| | }
|
| | }
|
| |
|
| | bool FileInfo::createDirectory() const
|
| | {
|
| | try {
|
| | fs::path path(stringToPath(FileName));
|
| | return fs::create_directory(path);
|
| | }
|
| | catch (const fs::filesystem_error&) {
|
| | return false;
|
| | }
|
| | }
|
| |
|
| | bool FileInfo::createDirectories() const
|
| | {
|
| | try {
|
| | fs::path path(stringToPath(FileName));
|
| | if (fs::exists(path)) {
|
| | return true;
|
| | }
|
| |
|
| | return fs::create_directories(path);
|
| | }
|
| | catch (const fs::filesystem_error&) {
|
| | return false;
|
| | }
|
| | }
|
| |
|
| | bool FileInfo::deleteDirectory() const
|
| | {
|
| | if (!isDir()) {
|
| | return false;
|
| | }
|
| |
|
| | try {
|
| | fs::path path = stringToPath(FileName);
|
| | return fs::remove(path);
|
| | }
|
| | catch (const fs::filesystem_error& e) {
|
| | std::clog << e.what() << '\n';
|
| | return false;
|
| | }
|
| | }
|
| |
|
| | bool FileInfo::deleteDirectoryRecursive() const
|
| | {
|
| | if (!isDir()) {
|
| | return false;
|
| | }
|
| |
|
| | try {
|
| | fs::path path = stringToPath(FileName);
|
| | return fs::remove_all(path) > 0;
|
| | }
|
| | catch (const fs::filesystem_error& e) {
|
| | std::clog << e.what() << '\n';
|
| | return false;
|
| | }
|
| | }
|
| |
|
| | std::vector<Base::FileInfo> FileInfo::getDirectoryContent() const
|
| | {
|
| | std::error_code ec;
|
| | std::vector<Base::FileInfo> list;
|
| | fs::path path = stringToPath(FileName);
|
| |
|
| | for (const fs::directory_entry& f : fs::directory_iterator {path, ec}) {
|
| | list.emplace_back(pathToString(f.path()));
|
| | }
|
| |
|
| | return list;
|
| | }
|
| |
|