From 12acd1da6c728dbb016134f59dbdf297017d1257 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 22 Aug 2025 01:18:56 +0200 Subject: [PATCH] Base: Modernize FileInfo --- src/Base/FileInfo.cpp | 436 +++++++++++++----------------------------- src/Base/FileInfo.h | 2 - 2 files changed, 129 insertions(+), 309 deletions(-) diff --git a/src/Base/FileInfo.cpp b/src/Base/FileInfo.cpp index 024a190efd..f5370bfeb1 100644 --- a/src/Base/FileInfo.cpp +++ b/src/Base/FileInfo.cpp @@ -21,7 +21,6 @@ * * ***************************************************************************/ - #include "PreCompiled.h" #ifndef _PreComp_ @@ -29,38 +28,20 @@ #include #include #include -#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) -#include -#include -#elif defined(FC_OS_WIN32) -#include +#include +#ifdef FC_OS_WIN32 #include +#else +#include #endif -#include -#include #endif #include "FileInfo.h" #include "Exception.h" -#include "Stream.h" #include "TimeInfo.h" -#include "Tools.h" - using namespace Base; - -#ifndef R_OK -#define R_OK 4 /* Test for read permission */ -#endif -#ifndef W_OK -#define W_OK 2 /* Test for write permission */ -#endif -#ifndef X_OK -#define X_OK 1 /* Test for execute permission */ -#endif -#ifndef F_OK -#define F_OK 0 /* Test for existence */ -#endif +namespace fs = std::filesystem; //********************************************************************************** // helper @@ -109,30 +90,9 @@ const std::string& FileInfo::getTempPath() static std::string tempPath; if (tempPath.empty()) { -#ifdef FC_OS_WIN32 - wchar_t buf[MAX_PATH + 2]; - GetTempPathW(MAX_PATH + 1, buf); - int neededSize = WideCharToMultiByte(CP_UTF8, 0, buf, -1, 0, 0, 0, 0); - char* dest = new char[static_cast(neededSize)]; - WideCharToMultiByte(CP_UTF8, 0, buf, -1, dest, neededSize, 0, 0); - tempPath = dest; - delete[] dest; -#else - const char* tmp = getenv("TMPDIR"); - if (!Base::Tools::isNullOrEmpty(tmp)) { - tempPath = tmp; - FileInfo fi(tempPath); - if (tempPath.empty() || !fi.isDir()) { // still empty or non-existent - tempPath = "/tmp/"; - } - else if (tempPath.at(tempPath.size() - 1) != '/') { - tempPath.append("/"); - } - } - else { - tempPath = "/tmp/"; - } -#endif + fs::path tmp = fs::temp_directory_path(); + tmp += fs::path::preferred_separator; + tempPath = pathToString(tmp); } return tempPath; @@ -208,20 +168,20 @@ std::string FileInfo::getTempFileName(const char* FileName, const char* Path) #endif } -std::filesystem::path FileInfo::stringToPath(const std::string& str) +fs::path FileInfo::stringToPath(const std::string& str) { -#if defined(FC_OS_WIN32) +#ifdef FC_OS_WIN32 std::wstring_convert> converter; - std::filesystem::path path(converter.from_bytes(str)); + fs::path path(converter.from_bytes(str)); #else - std::filesystem::path path(str); + fs::path path(str); #endif return path; } -std::string FileInfo::pathToString(const std::filesystem::path& path) +std::string FileInfo::pathToString(const fs::path& path) { -#if defined(FC_OS_WIN32) +#ifdef FC_OS_WIN32 std::wstring_convert> converter; return converter.to_bytes(path.wstring()); #else @@ -266,15 +226,7 @@ std::string FileInfo::dirPath() const retval = FileName.substr(0, last_pos); } else { -#ifdef FC_OS_WIN32 - wchar_t buf[MAX_PATH + 1]; - GetCurrentDirectoryW(MAX_PATH, buf); - retval = std::string(ConvertFromWideString(std::wstring(buf))); -#else - char buf[PATH_MAX + 1]; - const char* cwd = getcwd(buf, PATH_MAX); - retval = std::string(cwd ? cwd : "."); -#endif + retval = pathToString(fs::current_path()); } return retval; } @@ -323,9 +275,9 @@ std::string FileInfo::completeExtension() const bool FileInfo::hasExtension(const char* Ext) const { -#if defined(FC_OS_WIN32) +#ifdef FC_OS_WIN32 return _stricmp(Ext, extension().c_str()) == 0; -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) +#else return strcasecmp(Ext, extension().c_str()) == 0; #endif } @@ -339,77 +291,62 @@ bool FileInfo::hasExtension(std::initializer_list Exts) const bool FileInfo::exists() const { -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - return _waccess(wstr.c_str(), F_OK) == 0; -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - return access(FileName.c_str(), F_OK) == 0; -#endif + fs::path path(stringToPath(FileName)); + return fs::exists(path); } bool FileInfo::isReadable() const { -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - return _waccess(wstr.c_str(), R_OK) == 0; -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - return access(FileName.c_str(), R_OK) == 0; -#endif + 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 FileInfo::isWritable() const { -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - return _waccess(wstr.c_str(), W_OK) == 0; -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - return access(FileName.c_str(), W_OK) == 0; -#endif + 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_write) == fs::perms::owner_write; } bool FileInfo::setPermissions(Permissions perms) { - int mode = 0; + fs::perms mode = fs::perms::none; if (perms & FileInfo::ReadOnly) { - mode |= S_IREAD; + mode |= fs::perms::owner_read; } if (perms & FileInfo::WriteOnly) { - mode |= S_IWRITE; + mode |= fs::perms::owner_write; } - if (mode == 0) { // bad argument + if (mode == fs::perms::none) { // bad argument return false; } -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - return _wchmod(wstr.c_str(), mode) == 0; -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - return chmod(FileName.c_str(), mode) == 0; -#endif + + 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 { - if (exists()) { -#ifdef FC_OS_WIN32 - - std::wstring wstr = toStdWString(); - FILE* fd = _wfopen(wstr.c_str(), L"rb"); - bool ok = (fd != 0); - if (fd) { - fclose(fd); - } - return ok; -#else - // clang-format off - struct stat st {}; - // clang-format on - if (stat(FileName.c_str(), &st) != 0) { - return false; - } - return S_ISREG(st.st_mode); -#endif + fs::path path = stringToPath(FileName); + if (fs::exists(path)) { + return fs::is_regular_file(path); } // TODO: Check for valid file name @@ -418,179 +355,108 @@ bool FileInfo::isFile() const bool FileInfo::isDir() const { - if (exists()) { - // if we can chdir then it must be a directory, otherwise we assume it - // is a file (which doesn't need to be true for any cases) -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - struct _stat st; - - if (_wstat(wstr.c_str(), &st) != 0) { - return false; - } - return ((st.st_mode & _S_IFDIR) != 0); - -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - struct stat st {}; - if (stat(FileName.c_str(), &st) != 0) { - return false; - } - return S_ISDIR(st.st_mode); -#else - return false; -#endif - } - else { - return false; + fs::path path = stringToPath(FileName); + if (fs::exists(path)) { + return fs::is_directory(path); } - // TODO: Check for valid path name - // return true; + return false; } unsigned int FileInfo::size() const { unsigned int bytes {}; - if (exists()) { - -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - struct _stat st; - if (_wstat(wstr.c_str(), &st) == 0) { - bytes = st.st_size; - } - -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - struct stat st {}; - if (stat(FileName.c_str(), &st) == 0) { - bytes = st.st_size; - } -#endif + fs::path path = stringToPath(FileName); + if (fs::exists(path)) { + bytes = fs::file_size(path); } + return bytes; } +template +std::time_t to_time_t(TP tp) +{ + using namespace std::chrono; + auto sctp = + time_point_cast(tp - TP::clock::now() + system_clock::now()); + return system_clock::to_time_t(sctp); +} + TimeInfo FileInfo::lastModified() const { TimeInfo ti = TimeInfo::null(); + if (exists()) { - -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - struct _stat st; - if (_wstat(wstr.c_str(), &st) == 0) { - ti.setTime_t(st.st_mtime); - } - -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - struct stat st {}; - if (stat(FileName.c_str(), &st) == 0) { - ti.setTime_t(st.st_mtime); - } -#endif + fs::path path = stringToPath(FileName); + ti.setTime_t(to_time_t(fs::last_write_time(path))); } - return ti; -} -TimeInfo FileInfo::lastRead() const -{ - TimeInfo ti = TimeInfo::null(); - if (exists()) { - -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - struct _stat st; - if (_wstat(wstr.c_str(), &st) == 0) { - ti.setTime_t(st.st_atime); - } - -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - struct stat st {}; - if (stat(FileName.c_str(), &st) == 0) { - ti.setTime_t(st.st_atime); - } -#endif - } return ti; } bool FileInfo::deleteFile() const { -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - return ::_wremove(wstr.c_str()) == 0; -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - return (::remove(FileName.c_str()) == 0); -#else -#error "FileInfo::deleteFile() not implemented for this platform!" -#endif + 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) { - bool res {}; -#if defined(FC_OS_WIN32) - std::wstring oldname = toStdWString(); - std::wstring newname = ConvertToWideString(NewName); - res = ::_wrename(oldname.c_str(), newname.c_str()) == 0; -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - res = ::rename(FileName.c_str(), NewName) == 0; -#else -#error "FileInfo::renameFile() not implemented for this platform!" -#endif - if (!res) { - int code = errno; - std::clog << "Error in renameFile: " << strerror(code) << " (" << code << ")" << std::endl; - } - else { + 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; } - - return res; } bool FileInfo::copyTo(const char* NewName) const { -#if defined(FC_OS_WIN32) - std::wstring oldname = toStdWString(); - std::wstring newname = ConvertToWideString(NewName); - return CopyFileW(oldname.c_str(), newname.c_str(), true) != 0; -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - FileInfo fi1(FileName); - FileInfo fi2(NewName); - Base::ifstream file(fi1, std::ios::in | std::ios::binary); - file.unsetf(std::ios_base::skipws); - Base::ofstream copy(fi2, std::ios::out | std::ios::binary); - file >> copy.rdbuf(); - return file.is_open() && copy.is_open(); -#else -#error "FileInfo::copyTo() not implemented for this platform!" -#endif + 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 { -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - return _wmkdir(wstr.c_str()) == 0; -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - return mkdir(FileName.c_str(), 0777) == 0; -#else -#error "FileInfo::createDirectory() not implemented for this platform!" -#endif + try { + fs::path path(stringToPath(FileName)); + return fs::create_directory(path); + } + catch (const fs::filesystem_error&) { + return false; + } } bool FileInfo::createDirectories() const { try { - std::filesystem::path path(stringToPath(FileName)); - if (std::filesystem::exists(path)) { + fs::path path(stringToPath(FileName)); + if (fs::exists(path)) { return true; } - std::filesystem::create_directories(path); - return true; + + return fs::create_directories(path); } - catch (const std::filesystem::filesystem_error&) { + catch (const fs::filesystem_error&) { return false; } } @@ -600,14 +466,15 @@ bool FileInfo::deleteDirectory() const if (!isDir()) { return false; } -#if defined(FC_OS_WIN32) - std::wstring wstr = toStdWString(); - return _wrmdir(wstr.c_str()) == 0; -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - return rmdir(FileName.c_str()) == 0; -#else -#error "FileInfo::rmdir() not implemented for this platform!" -#endif + + 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 @@ -615,71 +482,26 @@ bool FileInfo::deleteDirectoryRecursive() const if (!isDir()) { return false; } - std::vector List = getDirectoryContent(); - for (Base::FileInfo& fi : List) { - if (fi.isDir()) { - // At least on Linux, directory needs execute permission to be - // deleted. We don't really need to set permission for directory - // anyway, since FC code does not touch directory permission. - // - // It->setPermissions(FileInfo::ReadWrite); - - fi.deleteDirectoryRecursive(); - } - else if (fi.isFile()) { - fi.setPermissions(FileInfo::ReadWrite); - fi.deleteFile(); - } - else { - throw Base::FileException( - "FileInfo::deleteDirectoryRecursive(): Unknown object Type in directory!"); - } + 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; } - return deleteDirectory(); } std::vector FileInfo::getDirectoryContent() const { - std::vector List; -#if defined(FC_OS_WIN32) - struct _wfinddata_t dentry; + std::error_code ec; + std::vector list; + fs::path path = stringToPath(FileName); - intptr_t hFile; - - // Find first directory entry - std::wstring wstr = toStdWString(); - wstr += L"/*"; - - if ((hFile = _wfindfirst(wstr.c_str(), &dentry)) == -1L) { - return List; + for (const fs::directory_entry& f : fs::directory_iterator {path, ec}) { + list.emplace_back(pathToString(f.path())); } - while (_wfindnext(hFile, &dentry) == 0) { - if (wcscmp(dentry.name, L"..") != 0) { - List.push_back( - FileInfo(FileName + "/" + ConvertFromWideString(std::wstring(dentry.name)))); - } - } - - _findclose(hFile); - -#elif defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - DIR* dp(nullptr); - struct dirent* dentry(nullptr); - if (!(dp = opendir(FileName.c_str()))) { - return List; - } - - while ((dentry = readdir(dp))) { - std::string dir = dentry->d_name; - if (dir != "." && dir != "..") { - List.emplace_back(FileName + "/" + dir); - } - } - closedir(dp); -#else -#error "FileInfo::getDirectoryContent() not implemented for this platform!" -#endif - return List; + return list; } diff --git a/src/Base/FileInfo.h b/src/Base/FileInfo.h index e27baa9cf3..1e8a05b71a 100644 --- a/src/Base/FileInfo.h +++ b/src/Base/FileInfo.h @@ -127,8 +127,6 @@ public: unsigned int size() const; /// Returns the time when the file was last modified. TimeInfo lastModified() const; - /// Returns the time when the file was last read (accessed). - TimeInfo lastRead() const; //@} /** @name Directory management*/