diff --git a/mario/filesystem.hpp b/mario/filesystem.hpp new file mode 100644 index 0000000..7a71f62 --- /dev/null +++ b/mario/filesystem.hpp @@ -0,0 +1,127 @@ +#ifndef FSLIB_H +#define FSLIB_H + +#include + +#ifdef _WIN32 + +#include + +using string = std::wstring; +using char_t = wchar_t; + +#else + +#include + +using string = std::string; +using char_t = char; + +#endif + +// windows version stolen from +// http://www.martinbroadhurst.com/list-the-files-in-a-directory-in-c.html + +namespace FSLib { + +bool exists( const string &path ); +bool isDirectory( const string &path ); +bool rename( const string &file_a, const string &file_b ); +bool deleteFile( const string &file ); +string canonical( const string &path ); +bool createDirectoryFull( const string &path ); +string getContainingDirectory( const string &path ); +string getFileName( const string &path ); +string getFileExtension( const string &path ); +extern char dir_divisor; + +class Directory { +public: + Directory() = delete; + explicit Directory( const string &path_ ); + explicit Directory( const Directory &d ) = default; + explicit Directory( Directory &&d ) = default; + + class Iterator { + public: + explicit Iterator( const Directory &d_ ); + ~Iterator(); + +#ifdef _WIN32 + explicit Iterator( bool ended_ ); +#else + Iterator( const Directory &d_, const struct dirent *current_entry_ ); +#endif + + Iterator() = delete; + + Iterator( const Iterator &i ) = default; + + Iterator( Iterator &&i ) = default; + + char_t const *operator*() const; + + Iterator &operator++(); + + bool operator==( const Iterator &i_other ) const; + + Iterator operator++( int ) { + Iterator ret( *this ); + operator++(); + return ret; + } + + bool operator!=( const Iterator &i_other ) const { + return !( i_other == *this ); + } + + private: +#ifndef _WIN32 + DIR *d{}; + const struct dirent *current_entry{}; +#else + HANDLE hFind{}; + WIN32_FIND_DATA data{}; + bool ended{ false }; +#endif + }; + + using iterator = Iterator; + using const_iterator = Iterator; + + iterator end(); + + const_iterator end() const; + + iterator begin() { + return Iterator( *this ); + } + + const_iterator begin() const { + return Iterator( *this ); + } + + const_iterator cbegin() const { + return begin(); + } + + const_iterator cend() const { + return end(); + } + + const char_t *path() const { + return dir_path.c_str(); + } + +#ifdef _WIN32 + const char_t *validPath() const { + return dir_path.substr( 0, dir_path.length() - 2 ).c_str(); + } +#endif + +private: + string dir_path; +}; +} // namespace FSLib + +#endif diff --git a/mario/filesystem/unix/filesystem.cpp b/mario/filesystem/unix/filesystem.cpp new file mode 100644 index 0000000..32db164 --- /dev/null +++ b/mario/filesystem/unix/filesystem.cpp @@ -0,0 +1,174 @@ +#include "../../filesystem.hpp" +#include +#include +#include +#ifdef __APPLE__ +#include +#endif +#include +#include + +char FSLib::dir_divisor = '/'; + +FSLib::Directory::Directory( const string &path_ ) : dir_path( path_ ) {} + +FSLib::Directory::Iterator::Iterator( const Directory &d_ ) + : d( opendir( d_.path() ) ) { + if ( !exists( d_.path() ) || !isDirectory( d_.path() ) ) { + throw std::runtime_error( + std::string( "Directory " ) + d_.path() + + " either doesn't exist or isn't a directory" ); + } + + current_entry = readdir( d ); + + // skip "." and ".." + if ( current_entry != nullptr && + ( !strcmp( current_entry->d_name, "." ) || + !strcmp( current_entry->d_name, ".." ) ) ) + ++( *this ); +} + +FSLib::Directory::Iterator::Iterator( const Directory &d_, + const struct dirent *current_entry_ ) + : d( opendir( d_.path() ) ), current_entry( current_entry_ ) {} + +FSLib::Directory::Iterator::~Iterator() { + if ( d ) + closedir( d ); +} + +bool FSLib::exists( const string &path ) { + struct stat path_stat; + return stat( path.c_str(), &path_stat ) == 0; +} + +string FSLib::canonical( const string &path ) { + char_t *canonical_path = new char_t[PATH_MAX]; + auto failed = realpath( path.c_str(), canonical_path ) == nullptr; + + if ( failed ) { + delete[] canonical_path; + return string(); + } + + string canonical_string{ canonical_path }; + delete[] canonical_path; + return canonical_string; +} + +bool FSLib::isDirectory( const string &path ) { + struct stat path_stat; + + if ( stat( path.c_str(), &path_stat ) != 0 ) + return false; + + return S_ISDIR( path_stat.st_mode ); +} + +bool FSLib::rename( const string &file_a, const string &file_b ) { + // TODO log + std::cout << file_a << " -> " << file_b << std::endl; + return ::rename( file_a.c_str(), file_b.c_str() ) == 0; +} + +// TODO do windows version +bool deleteRecursive(const string &dir) { + for(const auto &file : FSLib::Directory(dir)) { + auto path = dir + "/" + file; + if(FSLib::isDirectory(path)) { + if(!deleteRecursive(path)) { + return false; + } + } else if(unlink(path.c_str()) != 0) { + return false; + } + } + return rmdir(dir.c_str()) == 0; +} + +bool FSLib::deleteFile( const string &file ) { + // TODO log + auto canon = canonical( file ); + if(canon.empty()) { + return false; + } + + if(isDirectory(canon)) { + return deleteRecursive(canon); + } + return unlink(canon.c_str()) == 0; +} + +bool FSLib::createDirectoryFull( const string &path ) { + uint64_t pos{}; + // go through all directories leading to the last one + // and create them if they don't exist + do { + // get partial directory path + pos = path.find_first_of( "/", pos ); + if ( pos > 0 ) { + auto dirname = path.substr( 0, pos ); + // create it if it doesn't exist + if ( !FSLib::exists( dirname ) ) { + if ( mkdir( dirname.c_str(), + S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) != 0 ) + return false; + } + } + pos++; + } while ( pos < path.length() && pos != 0 ); + return true; +} + +FSLib::Directory::iterator FSLib::Directory::end() { + return Iterator( *this, nullptr ); +} + +FSLib::Directory::const_iterator FSLib::Directory::end() const { + return Iterator( *this, nullptr ); +} + +char_t const *FSLib::Directory::Iterator::operator*() const { + return current_entry->d_name; +} + +FSLib::Directory::Iterator &FSLib::Directory::Iterator::operator++() { + if ( current_entry == nullptr ) + return *this; + current_entry = readdir( d ); + // skip . and .. + if ( current_entry != nullptr && + ( !strcmp( current_entry->d_name, "." ) || + !strcmp( current_entry->d_name, ".." ) ) ) + return operator++(); + return *this; +} + +bool FSLib::Directory::Iterator::operator==( const Iterator &i_other ) const { + return i_other.current_entry == current_entry; +} + +string FSLib::getContainingDirectory( const string &path ) { + auto pos = path.find_last_of('/'); + if(pos == string::npos) { + return "."; + } + return path.substr(0, pos); +} + +string FSLib::getFileName( const string &path ) { + auto pos = path.find_last_of('/'); + if(pos == string::npos) { + return path; + } + return path.substr(pos+1); +} + +string FSLib::getFileExtension( const string &path ) { + auto pos = path.find_last_of('.'); + if(pos == string::npos) { + return ""; + } + return path.substr(pos + 1); +} diff --git a/mario/filesystem/windows/filesystem.cpp b/mario/filesystem/windows/filesystem.cpp new file mode 100644 index 0000000..098d115 --- /dev/null +++ b/mario/filesystem/windows/filesystem.cpp @@ -0,0 +1,156 @@ +#include "../../filesystem.hpp" +#include +#include +#include +#include + +char FSLib::dir_divisor = '\\'; + +FSLib::Directory::Directory( const string &path_ ) : dir_path( path_ ) { + // need to append \\* for windows to search files in directory + dir_path.append( L"\\*" ); +} + +FSLib::Directory::Iterator::Iterator( const Directory &d_ ) { + if ( !exists( d_.validPath() ) || !isDirectory( d_.validPath() ) ) { + throw std::runtime_error( + "Directory either doesn't exist or isn't a directory" ); + } + hFind = FindFirstFileW( d_.path(), &data ); + if ( hFind != INVALID_HANDLE_VALUE ) { + if ( !wcscmp( data.cFileName, L"." ) || + !wcscmp( data.cFileName, L".." ) ) { + ++( *this ); + } + } else { + ended = true; + } +} + +FSLib::Directory::Iterator::~Iterator() { + if ( hFind ) + FindClose( hFind ); +} + +// this is definitely not a good way to create the "end" iterator +// but it was the only way I thought of with my limited knowledge of +// windows.h +FSLib::Directory::Iterator::Iterator( bool ended_ ) : ended( ended_ ) {} + +bool FSLib::exists( const string &path ) { + struct _stat path_stat; + return _wstat( path.c_str(), &path_stat ) == 0; +} + +string FSLib::canonical( const string &path ) { + char_t *full_path = new char_t[MAX_PATH]; + char_t *canonical_path = new char_t[MAX_PATH]; + + auto failed = !GetFullPathName( path.c_str(), MAX_PATH, full_path, NULL ); + + if ( failed ) { + delete[] canonical_path; + delete[] full_path; + return string(); + } + + failed = !PathCanonicalizeW( canonical_path, full_path ); + + delete[] full_path; + + if ( failed ) { + delete[] canonical_path; + return string(); + } + + string canonical_string{ canonical_path }; + delete[] canonical_path; + return canonical_string; +} + +bool FSLib::isDirectory( const string &path ) { + struct _stat path_stat; + + if ( _wstat( path.c_str(), &path_stat ) != 0 ) + return false; + + return path_stat.st_mode & _S_IFDIR; +} + +bool FSLib::rename( const string &file_a, const string &file_b ) { + return MoveFileExW( file_a.c_str(), file_b.c_str(), + MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING ); +} + +bool FSLib::createDirectoryFull( const string &path ) { + uint64_t pos = path.find_first_of( L":", 0 ) + 2; + if ( pos == string::npos ) + pos = 0; + // go through all directories leading to the last one + // and create them if they don't exist + do { + // get partial directory path + pos = path.find_first_of( L"\\", pos ); + auto dirname = path.substr( 0, pos ); + // create it if it doesn't exist + if ( !FSLib::exists( dirname ) ) { + if ( !CreateDirectoryW( dirname.c_str(), NULL ) ) + return false; + } + pos++; + } while ( pos < path.length() && pos != 0 ); + return true; +} + +FSLib::Directory::iterator FSLib::Directory::end() { + return Iterator( true ); +} + +FSLib::Directory::const_iterator FSLib::Directory::end() const { + return Iterator( true ); +} + +char_t const *FSLib::Directory::Iterator::operator*() const { + return data.cFileName; +} + +FSLib::Directory::Iterator &FSLib::Directory::Iterator::operator++() { + if ( ended == true ) + return *this; + // skip . and .. + if ( FindNextFileW( hFind, &data ) == 0 ) { + ended = true; + } else if ( !wcscmp( data.cFileName, L"." ) || + !wcscmp( data.cFileName, L".." ) ) { + return operator++(); + } + return *this; +} + +bool FSLib::Directory::Iterator::operator==( const Iterator &i_other ) const { + return i_other.ended == ended; +} + +string FSLib::getContainingDirectory( const string &path ) { + auto pos = path.find_last_of('\\'); + if(pos == string::npos) { + return "."; + } + return path.substr(0, pos); +} + +string FSLib::getFileName( const string &path ) { + auto pos = path.find_last_of('\\'); + if(pos == string::npos) { + return path; + } + return path.substr(pos+1); +} + +string FSLib::getFileExtension( const string &path ) { + auto pos = path.find_last_of('.'); + if(pos == string::npos) { + return ""; + } + return path.substr(pos + 1); +}