/* * Author: Patrick-Christopher Mattulat * Company: Lynar Studios * E-Mail: webmaster@lynarstudios.com * Created: 2020-08-15 * Changed: 2025-12-21 * * */ #include #include #ifdef _WIN32 #include #endif #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #endif #if defined(unix) || defined(__APPLE__) #include #endif #include using ls::std::core::Class; using ls::std::core::FileOperationException; using ls::std::io::File; using ls::std::io::FilePathSeparator; using ls::std::io::FilePathSeparatorMatch; using std::accumulate; using std::find_if; using std::getline; using std::ifstream; using std::ios; using std::list; using std::move; using std::ofstream; using std::remove; using std::remove_if; using std::rename; using std::replace; using std::streamoff; using std::streampos; using std::string; using std::stringstream; using std::vector; File::File(string _absoluteFilePath) : Class("File"), absoluteFilePath(_normalizePath(::move(_absoluteFilePath))) {} File::~File() noexcept = default; bool File::operator==(const File &_file) const { return _equals(*this, _file); } bool File::operator!=(const File &_file) const { return !_equals(*this, _file); } bool File::canExecute() const { return _isExecutable(this->absoluteFilePath); } bool File::canRead() const { bool readable{}; #if defined(unix) || defined(__APPLE__) readable = _isReadableUnix(this->absoluteFilePath); #endif #ifdef _WIN32 readable = File::_isReadableWindows(this->absoluteFilePath); #endif return readable; } bool File::canWrite() const { return _isWritable(this->absoluteFilePath); } void File::createNewFile() const { if (!_exists(this->absoluteFilePath)) { ofstream file{this->absoluteFilePath}; file.close(); } else { throw FileOperationException{R"lit(file ")lit" + this->absoluteFilePath + R"lit(" could not be created!)lit"}; } } bool File::exists() const { return _exists(this->absoluteFilePath); } string File::getAbsoluteFilePath() const { return this->absoluteFilePath; } string File::getName() const { string copy = this->absoluteFilePath; // if it's a directory, remove separator from end, if it does exist if (_isDirectory(this->absoluteFilePath)) { copy.erase(remove_if(copy.end() - 1, copy.end(), FilePathSeparatorMatch()), copy.end()); } // now get the file / directory name const auto base = find_if(copy.rbegin(), copy.rend(), FilePathSeparatorMatch()).base(); return string{base, copy.end()}; } string File::getParent() const { return _getParent(this->absoluteFilePath); } string File::getWorkingDirectory() { string workingDirectory{}; #if defined(unix) || defined(__APPLE__) workingDirectory = _getWorkingDirectoryUnix(); #endif #ifdef _WIN32 workingDirectory = File::_getWorkingDirectoryWindows(); #endif return workingDirectory; } size_t File::getSize() const { streampos fileSize{}; if (_exists(this->absoluteFilePath)) { ifstream fileHandler{this->absoluteFilePath, ios::in}; fileSize = fileHandler.tellg(); fileHandler.seekg(0, ios::end); fileSize = fileHandler.tellg() - fileSize; fileHandler.close(); } const auto off = static_cast(fileSize); return static_cast(off); // not redundant ;) } bool File::isDirectory() const { return _isDirectory(this->absoluteFilePath); } bool File::isFile() const { return _isFile(this->absoluteFilePath); } time_t File::lastModified() const { return _lastModified(this->absoluteFilePath); } ::list File::list() const { ::list fileList{}; if (_isDirectory(this->absoluteFilePath)) { fileList = _list(this->absoluteFilePath); } return fileList; } ::list File::listFiles() const { ::list fileList{}; if (_isDirectory(this->absoluteFilePath)) { fileList = _listFiles(this->absoluteFilePath); } return fileList; } void File::makeDirectory() const { if (!_makeDirectory(this->absoluteFilePath)) { throw FileOperationException{R"lit(directory ")lit" + this->absoluteFilePath + R"lit(" could not be created!)lit"}; } } void File::makeDirectories() const { const vector subDirectories = _splitIntoSubDirectoryNames(this->absoluteFilePath); const char separator = FilePathSeparator::get(); string currentHierarchy{}; for (const auto &subDirectory : subDirectories) { currentHierarchy += subDirectory; if (!_exists(currentHierarchy + separator) && !currentHierarchy.empty() && !_makeDirectory(currentHierarchy)) { throw FileOperationException{"operation: create directory"}; } currentHierarchy += separator; } } void File::remove() const { if (_isFile(this->absoluteFilePath)) { ::remove(this->absoluteFilePath.c_str()); } if (_isDirectory(this->absoluteFilePath)) { _remove(this->absoluteFilePath); } } bool File::renameTo(const string &_newName) { const bool renamed = _renameTo(this->absoluteFilePath, _newName); if (renamed) { this->absoluteFilePath = _newName; } return renamed; } void File::reset(const string &_newPath) { this->absoluteFilePath = _normalizePath(_newPath); } #ifdef _WIN32 void File::_addToFileListWindows(const string &_path, bool _withDirectories, WIN32_FIND_DATA _data, ::list &_list) { const char separator = FilePathSeparator::get(); string absolutePath = _path + separator + _data.cFileName; if (_withDirectories) { _list.emplace_back(absolutePath); } else { if (File::_isFile(absolutePath)) { _list.emplace_back(absolutePath); } } } #endif #if defined(unix) || defined(__APPLE__) void File::_addToFileListUnix(const string &_path, const bool _withDirectories, const dirent *directoryEntity, ::list &_list) { const char separator = FilePathSeparator::get(); string absolutePath = _path + separator + directoryEntity->d_name; if (_withDirectories) { _list.emplace_back(absolutePath); } else { if (_isFile(absolutePath)) { _list.emplace_back(absolutePath); } } } #endif bool File::_equals(const File &_file, const File &_foreignFile) { bool isEqual = _file.getAbsoluteFilePath() == _foreignFile.getAbsoluteFilePath(); if (_file.exists() && _foreignFile.exists()) { isEqual = isEqual && _file.canRead() == _foreignFile.canRead(); isEqual = isEqual && _file.canWrite() == _foreignFile.canWrite(); isEqual = isEqual && _file.canExecute() == _foreignFile.canExecute(); } return isEqual; } bool File::_exists(const string &_path) { struct stat _stat { }; return (stat(_path.c_str(), &_stat) == 0); } string File::_getParent(const string &_path) { vector subDirectoryNames = File::_splitIntoSubDirectoryNames(_path); const char separator = FilePathSeparator::get(); subDirectoryNames.pop_back(); return accumulate(subDirectoryNames.begin(), subDirectoryNames.end(), string{}, [separator](string parent, const string &subDirectoryName) { return ::move(parent) + subDirectoryName + separator; }); } #if defined(unix) || defined(__APPLE__) string File::_getWorkingDirectoryUnix() { string workingDirectory{}; if (string buffer(PATH_MAX, 'x'); getcwd(buffer.data(), buffer.size()) == nullptr) { throw FileOperationException{"operation: get working directory"}; } else { workingDirectory = string(buffer); } return workingDirectory; } #endif #ifdef _WIN32 string File::_getWorkingDirectoryWindows() { string workingDirectory{}; TCHAR buffer[MAX_PATH]; if (!GetCurrentDirectory(MAX_PATH, buffer)) { throw FileOperationException{"operation: get working directory"}; } else { workingDirectory = string(buffer); } return workingDirectory; } #endif bool File::_isDirectory(const string &_path) { bool match{}; if (struct stat _stat{}; stat(_path.c_str(), &_stat) == 0) { match = _stat.st_mode & static_cast(S_IFDIR); } return match; } bool File::_isExecutable(const string &_path) { bool executable{}; if (_exists(_path)) { struct stat _stat { }; if (stat(_path.c_str(), &_stat) == 0) { executable = (_stat.st_mode & static_cast(S_IEXEC)) != 0; } } return executable; } bool File::_isFile(const string &_path) { bool match{}; if (struct stat _stat{}; stat(_path.c_str(), &_stat) == 0) { match = _stat.st_mode & static_cast(S_IFREG); } return match; } #if defined(unix) || defined(__APPLE__) bool File::_isReadableUnix(const string &_path) { bool readable{}; if (_exists(_path)) { struct stat _stat { }; if (stat(_path.c_str(), &_stat) == 0) { readable = (_stat.st_mode & static_cast(S_IREAD)) != 0; } } else { throw FileOperationException{"operation: fetch permissions"}; } return readable; } #endif #ifdef _WIN32 bool File::_isReadableWindows(const string &_path) { bool readable; WIN32_FIND_DATA data{}; HANDLE handleFind = FindFirstFile(_path.c_str(), &data); if (handleFind != INVALID_HANDLE_VALUE) { readable = GetFileAttributes(data.cFileName) & (unsigned) FILE_ATTRIBUTE_READONLY; } else { throw FileOperationException{"operation: fetch permissions"}; } return readable; } #endif bool File::_isWritable(const string &_path) { bool writable{}; if (_exists(_path)) { struct stat _stat { }; if (stat(_path.c_str(), &_stat) == 0) { writable = (_stat.st_mode & static_cast(S_IWRITE)) != 0; } } return writable; } time_t File::_lastModified(const string &_path) { time_t lastModifiedTimeStamp{}; if (struct stat _stat{}; stat(_path.c_str(), &_stat) == 0) { lastModifiedTimeStamp = _stat.st_mtime; } return lastModifiedTimeStamp; } ::list File::_list(const string &_path) { ::list filesInDirectory{}; #if defined(unix) || defined(__APPLE__) filesInDirectory = _listUnix(_path, true); #endif #ifdef _WIN32 filesInDirectory = File::_listWindows(_path, true); #endif return filesInDirectory; } ::list File::_listFiles(const string &_path) { ::list filesInDirectory{}; #if defined(unix) || defined(__APPLE__) filesInDirectory = _listUnix(_path, false); #endif #ifdef _WIN32 filesInDirectory = File::_listWindows(_path, false); #endif return filesInDirectory; } #if defined(unix) || defined(__APPLE__) ::list File::_listUnix(const string &_path, const bool withDirectories) { ::list filesInDirectory{}; DIR *directory = opendir(_path.c_str()); const dirent *directoryEntity; string absolutePath{}; while ((directoryEntity = readdir(directory)) != nullptr) { _addToFileListUnix(_path, withDirectories, directoryEntity, filesInDirectory); } closedir(directory); return filesInDirectory; } #endif #ifdef _WIN32 ::list File::_listWindows(const string &_path, bool withDirectories) { ::list filesInDirectory{}; WIN32_FIND_DATA data{}; HANDLE hFind; string pattern{_path + FilePathSeparator::get() + "*"}; if ((hFind = FindFirstFile(pattern.c_str(), &data)) != INVALID_HANDLE_VALUE) { do { File::_addToFileListWindows(_path, withDirectories, data, filesInDirectory); } while (FindNextFile(hFind, &data) != 0); FindClose(hFind); } return filesInDirectory; } #endif bool File::_makeDirectory(const string &_path) { int result{}; #ifdef _WIN32 result = _mkdir(_path.c_str()); #endif #if defined(unix) || defined(__APPLE__) result = mkdir(_path.c_str(), 0777); #endif return result == 0; } string File::_normalizePath(string _path) { _path = _replaceWrongSeparator(_path); _path = _reduceSeparators(_path); return _path; } string File::_reduceSeparators(const string &_path) { static const char separator = {FilePathSeparator::get()}; string normalizedPath{}; size_t index{}; while (index < _path.size()) { if (_path[index] == separator) { normalizedPath += _path[index]; do { index++; } while (_path[index] == separator); } else { normalizedPath += _path[index]; index++; } } return normalizedPath; } void File::_remove(const string &_path) { #if defined(unix) || defined(__APPLE__) _removeUnix(_path); #endif #ifdef _WIN32 File::_removeWindows(_path); #endif } #if defined(unix) || defined(__APPLE__) void File::_removeUnix(const string &_path) { rmdir(_path.c_str()); } #endif #ifdef _WIN32 void File::_removeWindows(const string &_path) { _rmdir(_path.c_str()); } #endif bool File::_renameTo(const string &_oldName, const string &_newName) { return rename(_oldName.c_str(), _newName.c_str()) == 0; } string File::_replaceWrongSeparator(string _path) { static const char unixSeparator = FilePathSeparator::getUnixFilePathSeparator(); static const char windowsSeparator = FilePathSeparator::getWindowsFilePathSeparator(); #if defined(unix) || defined(__APPLE__) replace(_path.begin(), _path.end(), windowsSeparator, unixSeparator); #endif #ifdef _WIN32 replace(_path.begin(), _path.end(), unixSeparator, windowsSeparator); #endif return _path; } vector File::_splitIntoSubDirectoryNames(const string &_path) { vector subDirectoryNames{}; stringstream _stream{_path}; string subDirectoryName{}; const char separator = FilePathSeparator::get(); while (getline(_stream, subDirectoryName, separator)) { subDirectoryNames.push_back(subDirectoryName); } return subDirectoryNames; }