diff --git a/src/common/repository/sqlite/SqliteDatabase.cxx b/src/common/repository/sqlite/SqliteDatabase.cxx index b761a5325..89353d4e0 100644 --- a/src/common/repository/sqlite/SqliteDatabase.cxx +++ b/src/common/repository/sqlite/SqliteDatabase.cxx @@ -16,6 +16,7 @@ //============================================================================ #include +#include #include "SqliteDatabase.hxx" #include "Logger.hxx" @@ -25,7 +26,8 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SqliteDatabase::SqliteDatabase(const string& databaseDirectory, const string& databaseName) - : myDatabaseFile{databaseDirectory + databaseName + ".sqlite3"} + : myDatabaseFile{(std::filesystem::path(databaseDirectory) / + (databaseName + ".sqlite3")).string()} { } diff --git a/src/emucore/FSNode.cxx b/src/emucore/FSNode.cxx index 03a13658f..11cf9727a 100644 --- a/src/emucore/FSNode.cxx +++ b/src/emucore/FSNode.cxx @@ -50,6 +50,12 @@ void FSNode::setPath(const string& path) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FSNode& FSNode::operator/=(const string& path) { +#if 0 + _fspath /= path; + setFlags(); + + return *this; +#else if (path != EmptyString) { string newPath = getPath(); @@ -60,6 +66,7 @@ FSNode& FSNode::operator/=(const string& path) } return *this; +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -232,8 +239,7 @@ const string& FSNode::getName() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FSNode::setName(const string& name) { - if (_realNode) - _realNode->setName(name); + if (_realNode) _realNode->setName(name); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -251,6 +257,15 @@ string FSNode::getShortPath() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - string FSNode::getNameWithExt(const string& ext) const { +#if 1 + fs::path p = getName(); + if (ext != EmptyString) + p.replace_extension(ext); + else + p.replace_extension(); + + return p.string(); +#else if (!_realNode) return EmptyString; @@ -260,6 +275,7 @@ string FSNode::getNameWithExt(const string& ext) const pos = s.find_last_of('.'); return (pos != string::npos) ? s.replace(pos, string::npos, ext) : s + ext; +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -335,117 +351,47 @@ size_t FSNode::getSize() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - size_t FSNode::read(ByteBuffer& buffer, size_t size) const { - size_t sizeRead = 0; - // File must actually exist if (!(exists() && isReadable())) throw runtime_error("File not found/readable"); - // First let the private subclass attempt to open the file + size_t sizeRead = 0; if (_realNode && (sizeRead = _realNode->read(buffer, size)) > 0) return sizeRead; - // Otherwise, the default behaviour is to read from a normal C++ ifstream - std::ifstream in(getPath(), std::ios::binary); - if (in) - { - in.seekg(0, std::ios::end); - sizeRead = static_cast(in.tellg()); - in.seekg(0, std::ios::beg); - - if (sizeRead == 0) - throw runtime_error("Zero-byte file"); - else if (size > 0) // If a requested size to read is provided, honour it - sizeRead = std::min(sizeRead, size); - - buffer = make_unique(sizeRead); - in.read(reinterpret_cast(buffer.get()), sizeRead); - } - else - throw runtime_error("File open/read error"); - - return sizeRead; + return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - size_t FSNode::read(stringstream& buffer) const { - size_t sizeRead = 0; - // File must actually exist if (!(exists() && isReadable())) throw runtime_error("File not found/readable"); - // First let the private subclass attempt to open the file + size_t sizeRead = 0; if (_realNode && (sizeRead = _realNode->read(buffer)) > 0) return sizeRead; - // Otherwise, the default behaviour is to read from a normal C++ ifstream - // and convert to a stringstream - std::ifstream in(getPath()); - if (in) - { - in.seekg(0, std::ios::end); - sizeRead = static_cast(in.tellg()); - in.seekg(0, std::ios::beg); - - if (sizeRead == 0) - throw runtime_error("Zero-byte file"); - - buffer << in.rdbuf(); - } - else - throw runtime_error("File open/read error"); - - return sizeRead; + return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - size_t FSNode::write(const ByteBuffer& buffer, size_t size) const { size_t sizeWritten = 0; - - // First let the private subclass attempt to open the file if (_realNode && (sizeWritten = _realNode->write(buffer, size)) > 0) return sizeWritten; - // Otherwise, the default behaviour is to write to a normal C++ ofstream - std::ofstream out(getPath(), std::ios::binary); - if (out) - { - out.write(reinterpret_cast(buffer.get()), size); - - out.seekp(0, std::ios::end); - sizeWritten = static_cast(out.tellp()); - out.seekp(0, std::ios::beg); - } - else - throw runtime_error("File open/write error"); - - return sizeWritten; + return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - size_t FSNode::write(const stringstream& buffer) const { size_t sizeWritten = 0; - - // First let the private subclass attempt to open the file if (_realNode && (sizeWritten = _realNode->write(buffer)) > 0) return sizeWritten; - // Otherwise, the default behaviour is to write to a normal C++ ofstream - std::ofstream out(getPath()); - if (out) - { - out << buffer.rdbuf(); - - out.seekp(0, std::ios::end); - sizeWritten = static_cast(out.tellp()); - out.seekp(0, std::ios::beg); - } - else - throw runtime_error("File open/write error"); - - return sizeWritten; + return 0; } diff --git a/src/emucore/FSNode.hxx b/src/emucore/FSNode.hxx index 21555e760..a84d64952 100644 --- a/src/emucore/FSNode.hxx +++ b/src/emucore/FSNode.hxx @@ -18,6 +18,7 @@ #ifndef FS_NODE_HXX #define FS_NODE_HXX +#include #include #include "bspf.hxx" @@ -34,6 +35,7 @@ class FSNode; class AbstractFSNode; using AbstractFSNodePtr = shared_ptr; +namespace fs = std::filesystem; /** * List of multiple file system nodes. E.g. the contents of a given directory. @@ -462,7 +464,7 @@ class AbstractFSNode * This method can throw exceptions, and should be used inside * a try-catch block. */ - virtual size_t read(ByteBuffer& buffer, size_t size) const { return 0; } + virtual size_t read(ByteBuffer& buffer, size_t size) const = 0; /** * Read data (text format) into the given stream. @@ -473,7 +475,7 @@ class AbstractFSNode * This method can throw exceptions, and should be used inside * a try-catch block. */ - virtual size_t read(stringstream& buffer) const { return 0; } + virtual size_t read(stringstream& buffer) const = 0; /** * Write data (binary format) from the given buffer. @@ -485,7 +487,7 @@ class AbstractFSNode * This method can throw exceptions, and should be used inside * a try-catch block. */ - virtual size_t write(const ByteBuffer& buffer, size_t size) const { return 0; } + virtual size_t write(const ByteBuffer& buffer, size_t size) const = 0; /** * Write data (text format) from the given stream. @@ -496,7 +498,7 @@ class AbstractFSNode * This method can throw exceptions, and should be used inside * a try-catch block. */ - virtual size_t write(const stringstream& buffer) const { return 0; } + virtual size_t write(const stringstream& buffer) const = 0; /** * Returns the last component of a given path. diff --git a/src/unix/FSNodePOSIX.cxx b/src/unix/FSNodePOSIX.cxx index de143da54..fbeb7198a 100644 --- a/src/unix/FSNodePOSIX.cxx +++ b/src/unix/FSNodePOSIX.cxx @@ -37,7 +37,11 @@ FSNodePOSIX::FSNodePOSIX(const string& path, bool verify) // Expand '~' to the HOME environment variable if(_path[0] == '~') { + #if defined(BSPF_WINDOWS) + + #else const char* home = std::getenv("HOME"); + #endif if (home != nullptr) _path.replace(0, 1, home); } @@ -49,7 +53,9 @@ FSNodePOSIX::FSNodePOSIX(const string& path, bool verify) _path = buf.data(); } - _displayName = lastPathComponent(_path); + _fspath = _path; + _displayName = _fspath.filename(); + _path = _fspath.string(); if(verify) setFlags(); @@ -58,6 +64,34 @@ FSNodePOSIX::FSNodePOSIX(const string& path, bool verify) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FSNodePOSIX::setFlags() { +#if 1 +// cerr << "_fspath: " << _fspath << endl; + std::error_code ec; + const auto s = fs::status(_fspath, ec); + if (!ec) + { + const auto p = s.permissions(); + + _isValid = true; + _isFile = fs::is_regular_file(s); + _isDirectory = fs::is_directory(s); + _isReadable = (p & (fs::perms::owner_read | + fs::perms::group_read | + fs::perms::others_read)) != fs::perms::none; + _isWriteable = (p & (fs::perms::owner_write | + fs::perms::group_write | + fs::perms::others_write)) != fs::perms::none; +// cerr << "_isValid: " << _isValid << endl +// << "_isFile: " << _isFile << endl +// << "_isDirectory: " << _isDirectory << endl +// << "_isReadable: " << _isReadable << endl +// << "_isWriteable: " << _isWriteable << endl +// << endl; + } + else + _isValid = _isFile = _isDirectory = _isReadable = _isWriteable = false; + +#else struct stat st; _isValid = (0 == stat(_path.c_str(), &st)); @@ -72,13 +106,18 @@ void FSNodePOSIX::setFlags() } else _isDirectory = _isFile = false; +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - string FSNodePOSIX::getShortPath() const { // If the path starts with the home directory, replace it with '~' +#if defined(BSPF_WINDOWS) + +#else const char* env_home = std::getenv("HOME"); +#endif const string& home = env_home != nullptr ? env_home : EmptyString; if(home != EmptyString && BSPF::startsWithIgnoreCase(_path, home)) @@ -101,6 +140,7 @@ bool FSNodePOSIX::hasParent() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool FSNodePOSIX::getChildren(AbstractFSList& myList, ListMode mode) const { +cerr << "getChildren: " << _path << endl; assert(_isDirectory); DIR* dirp = opendir(_path.c_str()); @@ -187,6 +227,91 @@ size_t FSNodePOSIX::getSize() const return (stat(_path.c_str(), &st) == 0) ? st.st_size : 0; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +size_t FSNodePOSIX::read(ByteBuffer& buffer, size_t size) const +{ + size_t sizeRead = 0; + std::ifstream in(getPath(), std::ios::binary); + if (in) + { + in.seekg(0, std::ios::end); + sizeRead = static_cast(in.tellg()); + in.seekg(0, std::ios::beg); + + if (sizeRead == 0) + throw runtime_error("Zero-byte file"); + else if (size > 0) // If a requested size to read is provided, honour it + sizeRead = std::min(sizeRead, size); + + buffer = make_unique(sizeRead); + in.read(reinterpret_cast(buffer.get()), sizeRead); + } + else + throw runtime_error("File open/read error"); + + return sizeRead; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +size_t FSNodePOSIX::read(stringstream& buffer) const +{ + size_t sizeRead = 0; + std::ifstream in(getPath()); + if (in) + { + in.seekg(0, std::ios::end); + sizeRead = static_cast(in.tellg()); + in.seekg(0, std::ios::beg); + + if (sizeRead == 0) + throw runtime_error("Zero-byte file"); + + buffer << in.rdbuf(); + } + else + throw runtime_error("File open/read error"); + + return sizeRead; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +size_t FSNodePOSIX::write(const ByteBuffer& buffer, size_t size) const +{ + size_t sizeWritten = 0; + std::ofstream out(getPath(), std::ios::binary); + if (out) + { + out.write(reinterpret_cast(buffer.get()), size); + + out.seekp(0, std::ios::end); + sizeWritten = static_cast(out.tellp()); + out.seekp(0, std::ios::beg); + } + else + throw runtime_error("File open/write error"); + + return sizeWritten; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +size_t FSNodePOSIX::write(const stringstream& buffer) const +{ + size_t sizeWritten = 0; + std::ofstream out(getPath()); + if (out) + { + out << buffer.rdbuf(); + + out.seekp(0, std::ios::end); + sizeWritten = static_cast(out.tellp()); + out.seekp(0, std::ios::beg); + } + else + throw runtime_error("File open/write error"); + + return sizeWritten; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool FSNodePOSIX::makeDir() { diff --git a/src/unix/FSNodePOSIX.hxx b/src/unix/FSNodePOSIX.hxx index 67bf1b2f8..b10d6c0aa 100644 --- a/src/unix/FSNodePOSIX.hxx +++ b/src/unix/FSNodePOSIX.hxx @@ -39,9 +39,11 @@ #endif /* - * Implementation of the Stella file system API based on POSIX (for Linux and macOS) + * Implementation of the Stella filesystem API for regular files, + * based on the std::filesystem API in C++17. * - * Parts of this class are documented in the base interface class, AbstractFSNode. + * Parts of this class are documented in the base interface classes, + * AbstractFSNode and FSNode. See those classes for more information. */ class FSNodePOSIX : public AbstractFSNode { @@ -60,7 +62,7 @@ class FSNodePOSIX : public AbstractFSNode */ explicit FSNodePOSIX(const string& path, bool verify = true); - bool exists() const override { return access(_path.c_str(), F_OK) == 0; } + bool exists() const override { return fs::exists(_fspath); } const string& getName() const override { return _displayName; } void setName(const string& name) override { _displayName = name; } const string& getPath() const override { return _path; } @@ -68,21 +70,25 @@ class FSNodePOSIX : public AbstractFSNode bool hasParent() const override; bool isDirectory() const override { return _isDirectory; } bool isFile() const override { return _isFile; } - bool isReadable() const override { return access(_path.c_str(), R_OK) == 0; } - bool isWritable() const override { return access(_path.c_str(), W_OK) == 0; } + bool isReadable() const override { return _isReadable; } + bool isWritable() const override { return _isWriteable; } bool makeDir() override; bool rename(const string& newfile) override; size_t getSize() const override; + size_t read(ByteBuffer& buffer, size_t size) const override; + size_t read(stringstream& buffer) const override; + size_t write(const ByteBuffer& buffer, size_t size) const override; + size_t write(const stringstream& buffer) const override; + bool getChildren(AbstractFSList& list, ListMode mode) const override; AbstractFSNodePtr getParent() const override; protected: - string _path; - string _displayName; - bool _isValid{true}; - bool _isFile{false}; - bool _isDirectory{true}; + fs::path _fspath; + string _path, _displayName; + bool _isValid{false}, _isFile{false}, _isDirectory{false}, + _isReadable{false}, _isWriteable{false}; private: /**