diff --git a/.github/workflows/build-arduino-emulator.yml b/.github/workflows/build-arduino-emulator.yml index 259fa9b0..cd055007 100644 --- a/.github/workflows/build-arduino-emulator.yml +++ b/.github/workflows/build-arduino-emulator.yml @@ -42,7 +42,6 @@ jobs: - name: Clone AsyncTCP, Arduino FS headers, lwIP and lwIP contrib run: | git clone --depth 1 https://github.com/MitchBradley/PosixAsyncTCP .ci/asynctcp - git clone --depth 1 --branch "3.3.8" https://github.com/espressif/arduino-esp32.git .ci/arduino-esp32 - name: Build with Arduino-Emulator run: | diff --git a/examples/arduino_emulator/CMakeLists.txt b/examples/arduino_emulator/CMakeLists.txt index a6b3b925..abc5873b 100644 --- a/examples/arduino_emulator/CMakeLists.txt +++ b/examples/arduino_emulator/CMakeLists.txt @@ -3,10 +3,10 @@ project(espasyncwebserver_host_compile LANGUAGES C CXX) add_subdirectory(${CMAKE_SOURCE_DIR}/../../.ci/arduino-emulator ${CMAKE_BINARY_DIR}/arduino-emulator) file(GLOB WEB_SRC "${CMAKE_SOURCE_DIR}/../../src/*.cpp") +list(APPEND WEB_SRC "${CMAKE_SOURCE_DIR}/FS.cpp") add_library(espasyncwebserver STATIC ${WEB_SRC}) add_library(test STATIC ${CMAKE_SOURCE_DIR}/../../.ci/asynctcp/src/AsyncTCP.cpp - ${CMAKE_SOURCE_DIR}/../../.ci/arduino-esp32/libraries/FS/src/FS.cpp ${CMAKE_SOURCE_DIR}/../../.ci/arduino-emulator/ArduinoCore-Linux/cores/arduino/libb64/cencode.c ) @@ -14,14 +14,13 @@ target_compile_definitions(espasyncwebserver PUBLIC HOST ARDUINO=10813) target_include_directories(espasyncwebserver PUBLIC ${CMAKE_SOURCE_DIR}/../../src ${CMAKE_SOURCE_DIR}/../../.ci/asynctcp/src - ${CMAKE_SOURCE_DIR}/../../.ci/arduino-esp32/libraries/FS/src + ${CMAKE_SOURCE_DIR}/. ) target_link_libraries(espasyncwebserver PUBLIC arduino_emulator) target_compile_definitions(test PUBLIC HOST ARDUINO=10813) target_include_directories(test PUBLIC ${CMAKE_SOURCE_DIR}/../../.ci/asynctcp/src - ${CMAKE_SOURCE_DIR}/../../.ci/arduino-esp32/libraries/FS/src ) target_link_libraries(test PUBLIC arduino_emulator) diff --git a/examples/arduino_emulator/FS.cpp b/examples/arduino_emulator/FS.cpp new file mode 100644 index 00000000..eb347bd9 --- /dev/null +++ b/examples/arduino_emulator/FS.cpp @@ -0,0 +1,595 @@ +/* + FS.cpp - file system wrapper + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#define DEBUGV(...) \ + do { \ + } while (0) + +#include "FS.h" +#include "FSImpl.h" + +using namespace fs; + +static bool sflags(const char *mode, OpenMode &om, AccessMode &am); + +size_t File::write(uint8_t c) { + if (!_p) { + return 0; + } + + return _p->write(&c, 1); +} + +size_t File::write(const uint8_t *buf, size_t size) { + if (!_p) { + return 0; + } + + return _p->write(buf, size); +} + +int File::available() { + if (!_p) { + return false; + } + + return _p->size() - _p->position(); +} + +int File::availableForWrite() { + if (!_p) { + return false; + } + + return _p->availableForWrite(); +} + +int File::read() { + if (!_p) { + return -1; + } + + uint8_t result; + if (_p->read(&result, 1) != 1) { + return -1; + } + + return result; +} + +int File::read(uint8_t *buf, size_t size) { + if (!_p) { + return 0; + } + + return _p->read(buf, size); +} + +int File::peek() { + if (!_p) { + return -1; + } + + size_t curPos = _p->position(); + int result = read(); + seek(curPos, SeekSet); + return result; +} + +void File::flush() { + if (!_p) { + return; + } + + _p->flush(); +} + +bool File::seek(uint32_t pos, SeekMode mode) { + if (!_p) { + return false; + } + + return _p->seek(pos, mode); +} + +size_t File::position() const { + if (!_p) { + return 0; + } + + return _p->position(); +} + +size_t File::size() const { + if (!_p) { + return 0; + } + + return _p->size(); +} + +void File::close() { + if (_p) { + _p->close(); + _p = nullptr; + } +} + +File::operator bool() const { + return !!_p; +} + +bool File::truncate(uint32_t size) { + if (!_p) { + return false; + } + + return _p->truncate(size); +} + +const char *File::name() const { + if (!_p) { + return nullptr; + } + + return _p->name(); +} + +const char *File::fullName() const { + if (!_p) { + return nullptr; + } + + return _p->fullName(); +} + +bool File::isFile() const { + if (!_p) { + return false; + } + + return _p->isFile(); +} + +bool File::isDirectory() const { + if (!_p) { + return false; + } + + return _p->isDirectory(); +} + +void File::rewindDirectory() { + if (!_fakeDir) { + _fakeDir = std::make_shared(_baseFS->openDir(fullName())); + } else { + _fakeDir->rewind(); + } +} + +File File::openNextFile() { + if (!_fakeDir) { + _fakeDir = std::make_shared(_baseFS->openDir(fullName())); + } + _fakeDir->next(); + return _fakeDir->openFile("r"); +} + +String File::readString() { + String ret; + ret.reserve(size() - position()); + uint8_t temp[256]; + int countRead; + do { + countRead = read(temp, sizeof(temp)); + ret.concat(temp, countRead); + } while (countRead > 0); + return ret; +} + +time_t File::getLastWrite() { + if (!_p) { + return 0; + } + + return _p->getLastWrite(); +} + +time_t File::getCreationTime() { + if (!_p) { + return 0; + } + + return _p->getCreationTime(); +} + +void File::setTimeCallback(time_t (*cb)(void)) { + if (!_p) { + return; + } + _p->setTimeCallback(cb); + _timeCallback = cb; +} + +bool File::stat(FSStat *st) { + if (!_p) { + return false; + } + size_t pos = position(); + seek(0, SeekEnd); + st->size = position(); + seek(pos, SeekSet); + st->blocksize = 0; // Not set here + st->ctime = getCreationTime(); + st->atime = getLastWrite(); + st->isDir = isDirectory(); + return true; +} + +File Dir::openFile(const char *mode) { + if (!_impl) { + return File(); + } + + OpenMode om; + AccessMode am; + if (!sflags(mode, om, am)) { + DEBUGV("Dir::openFile: invalid mode `%s`\r\n", mode); + return File(); + } + + File f(_impl->openFile(om, am), _baseFS); + f.setTimeCallback(_timeCallback); + return f; +} + +String Dir::fileName() { + if (!_impl) { + return String(); + } + + return _impl->fileName(); +} + +time_t Dir::fileTime() { + if (!_impl) { + return 0; + } + return _impl->fileTime(); +} + +time_t Dir::fileCreationTime() { + if (!_impl) { + return 0; + } + return _impl->fileCreationTime(); +} + +size_t Dir::fileSize() { + if (!_impl) { + return 0; + } + + return _impl->fileSize(); +} + +bool Dir::isFile() const { + if (!_impl) { + return false; + } + + return _impl->isFile(); +} + +bool Dir::isDirectory() const { + if (!_impl) { + return false; + } + + return _impl->isDirectory(); +} + +bool Dir::next() { + if (!_impl) { + return false; + } + + return _impl->next(); +} + +bool Dir::rewind() { + if (!_impl) { + return false; + } + + return _impl->rewind(); +} + +void Dir::setTimeCallback(time_t (*cb)(void)) { + if (!_impl) { + return; + } + _impl->setTimeCallback(cb); + _timeCallback = cb; +} + +bool FS::setConfig(const FSConfig &cfg) { + if (!_impl) { + return false; + } + + return _impl->setConfig(cfg); +} + +bool FS::begin() { + if (!_impl) { + DEBUGV("#error: FS: no implementation"); + return false; + } + _impl->setTimeCallback(_timeCallback); + bool ret = _impl->begin(); + DEBUGV("%s\n", ret ? "" : "#error: FS could not start"); + return ret; +} + +void FS::end() { + if (_impl) { + _impl->end(); + } +} + +bool FS::gc() { + if (!_impl) { + return false; + } + return _impl->gc(); +} + +bool FS::check() { + if (!_impl) { + return false; + } + return _impl->check(); +} + +bool FS::format() { + if (!_impl) { + return false; + } + return _impl->format(); +} + +bool FS::info(FSInfo &info) { + if (!_impl) { + return false; + } + return _impl->info(info); +} + +File FS::open(const String &path, const char *mode) { + return open(path.c_str(), mode); +} + +File FS::open(const char *path, const char *mode) { + if (!_impl) { + return File(); + } + + OpenMode om; + AccessMode am; + if (!sflags(mode, om, am)) { + DEBUGV("FS::open: invalid mode `%s`\r\n", mode); + return File(); + } + File f(_impl->open(path, om, am), this); + f.setTimeCallback(_timeCallback); + return f; +} + +bool FS::exists(const char *path) { + if (!_impl) { + return false; + } + return _impl->exists(path); +} + +bool FS::exists(const String &path) { + return exists(path.c_str()); +} + +Dir FS::openDir(const char *path) { + if (!_impl) { + return Dir(); + } + DirImplPtr p = _impl->openDir(path); + Dir d(p, this); + d.setTimeCallback(_timeCallback); + return d; +} + +Dir FS::openDir(const String &path) { + return openDir(path.c_str()); +} + +bool FS::remove(const char *path) { + if (!_impl) { + return false; + } + return _impl->remove(path); +} + +bool FS::remove(const String &path) { + return remove(path.c_str()); +} + +bool FS::rmdir(const char *path) { + if (!_impl) { + return false; + } + return _impl->rmdir(path); +} + +bool FS::rmdir(const String &path) { + return rmdir(path.c_str()); +} + +bool FS::mkdir(const char *path) { + if (!_impl) { + return false; + } + return _impl->mkdir(path); +} + +bool FS::mkdir(const String &path) { + return mkdir(path.c_str()); +} + +bool FS::rename(const char *pathFrom, const char *pathTo) { + if (!_impl) { + return false; + } + return _impl->rename(pathFrom, pathTo); +} + +bool FS::rename(const String &pathFrom, const String &pathTo) { + return rename(pathFrom.c_str(), pathTo.c_str()); +} + +bool FS::stat(const char *path, FSStat *st) { + if (!_impl) { + return false; + } + return _impl->stat(path, st); +} + +bool FS::stat(const String &path, FSStat *st) { + return stat(path.c_str(), st); +} + +time_t FS::getCreationTime() { + if (!_impl) { + return 0; + } + return _impl->getCreationTime(); +} + +void FS::setTimeCallback(time_t (*cb)(void)) { + if (!_impl) { + return; + } + _impl->setTimeCallback(cb); + _timeCallback = cb; +} + +static bool sflags(const char *mode, OpenMode &om, AccessMode &am) { + switch (mode[0]) { + case 'r': + am = AM_READ; + om = OM_DEFAULT; + break; + case 'w': + am = AM_WRITE; + om = (OpenMode)(OM_CREATE | OM_TRUNCATE); + break; + case 'a': + am = AM_WRITE; + om = (OpenMode)(OM_CREATE | OM_APPEND); + break; + default: return false; + } + switch (mode[1]) { + case '+': am = (AccessMode)(AM_WRITE | AM_READ); break; + case 0: break; + default: return false; + } + return true; +} + +#if defined(FS_FREESTANDING_FUNCTIONS) + +/* + TODO: move these functions to public API: +*/ +File open(const char *path, const char *mode); +File open(String &path, const char *mode); + +Dir openDir(const char *path); +Dir openDir(String &path); + +template<> bool mount(FS &fs, const char *mountPoint); +/* +*/ + +struct MountEntry { + FSImplPtr fs; + String path; + MountEntry *next; +}; + +static MountEntry *s_mounted = nullptr; + +template<> bool mount(FS &fs, const char *mountPoint) { + FSImplPtr p = fs._impl; + if (!p || !p->mount()) { + DEBUGV("FSImpl mount failed\r\n"); + return false; + } + + !make sure mountPoint has trailing '/' here + + MountEntry *entry = new MountEntry; + entry->fs = p; + entry->path = mountPoint; + entry->next = s_mounted; + s_mounted = entry; + return true; +} + +/* + iterate over MountEntries and look for the ones which match the path +*/ +File open(const char *path, const char *mode) { + OpenMode om; + AccessMode am; + if (!sflags(mode, om, am)) { + DEBUGV("open: invalid mode `%s`\r\n", mode); + return File(); + } + + for (MountEntry *entry = s_mounted; entry; entry = entry->next) { + size_t offset = entry->path.length(); + if (strstr(path, entry->path.c_str())) { + File result = entry->fs->open(path + offset); + if (result) { + return result; + } + } + } + + return File(); +} + +File open(const String &path, const char *mode) { + return FS::open(path.c_str(), mode); +} + +Dir openDir(const String &path) { + return openDir(path.c_str()); +} +#endif diff --git a/examples/arduino_emulator/FS.h b/examples/arduino_emulator/FS.h new file mode 100644 index 00000000..0cd47add --- /dev/null +++ b/examples/arduino_emulator/FS.h @@ -0,0 +1,265 @@ +/* + FS.h - file system wrapper + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include +#include +#include <../include/time.h> // See issue #6714 + +class SDClass; + +namespace fs { + +class File; +class Dir; +class FS; + +class FileImpl; +typedef std::shared_ptr FileImplPtr; +class FSImpl; +typedef std::shared_ptr FSImplPtr; +class DirImpl; +typedef std::shared_ptr DirImplPtr; + +template bool mount(Tfs &fs, const char *mountPoint); + +enum SeekMode { + SeekSet = 0, + SeekCur = 1, + SeekEnd = 2 +}; + +struct FSStat { + size_t size; + size_t blocksize; + time_t ctime; + time_t atime; + bool isDir; +}; + +class File : public Stream { +public: + File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) { + _startMillis = millis(); /* workaround -O3 spurious warning #768 */ + } + + // Print methods: + size_t write(uint8_t) override; + size_t write(const uint8_t *buf, size_t size) override; + int availableForWrite() override; + + // Stream methods: + int available() override; + int read() override; + int peek() override; + void flush() override; + size_t readBytes(char *buffer, size_t length) { + return read((uint8_t *)buffer, length); + } + int read(uint8_t *buf, size_t size); + bool seek(uint32_t pos, SeekMode mode); + bool seek(uint32_t pos) { + return seek(pos, SeekSet); + } + size_t position() const; + size_t size() const; + virtual ssize_t streamRemaining() { + return (ssize_t)size() - (ssize_t)position(); + } + void close(); + operator bool() const; + const char *name() const; + const char *fullName() const; // Includes path + bool truncate(uint32_t size); + + bool isFile() const; + bool isDirectory() const; + + // Arduino "class SD" methods for compatibility + //TODO use stream::send / check read(buf,size) result + template size_t write(T &src) { + uint8_t obuf[256]; + size_t doneLen = 0; + size_t sentLen; + + while ((size_t)src.available() > sizeof(obuf)) { + src.read(obuf, sizeof(obuf)); + sentLen = write(obuf, sizeof(obuf)); + doneLen = doneLen + sentLen; + if (sentLen != sizeof(obuf)) { + return doneLen; + } + } + + size_t leftLen = src.available(); + src.read(obuf, leftLen); + sentLen = write(obuf, leftLen); + doneLen = doneLen + sentLen; + return doneLen; + } + using Print::write; + + void rewindDirectory(); + File openNextFile(); + + String readString(); + + time_t getLastWrite(); + time_t getCreationTime(); + void setTimeCallback(time_t (*cb)(void)); + + bool stat(FSStat *st); + +protected: + FileImplPtr _p; + time_t (*_timeCallback)(void) = nullptr; + + // Arduino SD class emulation + std::shared_ptr _fakeDir; + FS *_baseFS; +}; + +class Dir { +public: + Dir(DirImplPtr impl = DirImplPtr(), FS *baseFS = nullptr) : _impl(impl), _baseFS(baseFS) {} + + File openFile(const char *mode); + + String fileName(); + size_t fileSize(); + time_t fileTime(); + time_t fileCreationTime(); + bool isFile() const; + bool isDirectory() const; + + bool next(); + bool rewind(); + + void setTimeCallback(time_t (*cb)(void)); + +protected: + DirImplPtr _impl; + FS *_baseFS; + time_t (*_timeCallback)(void) = nullptr; +}; + +// Support > 4GB filesystems (SD, etc.) +struct FSInfo { + uint64_t totalBytes; + uint64_t usedBytes; + size_t blockSize; + size_t pageSize; + size_t maxOpenFiles; + size_t maxPathLength; +}; + +class FSConfig { +public: + static constexpr uint32_t FSId = 0x00000000; + + FSConfig(uint32_t type = FSId, bool autoFormat = true) : _type(type), _autoFormat(autoFormat) {} + + FSConfig setAutoFormat(bool val = true) { + _autoFormat = val; + return *this; + } + + uint32_t _type; + bool _autoFormat; +}; + +class FS { +public: + FS(FSImplPtr impl) : _impl(impl) { + _timeCallback = _defaultTimeCB; + } + + bool setConfig(const FSConfig &cfg); + + bool begin(); + void end(); + + bool format(); + bool info(FSInfo &info); + + File open(const char *path, const char *mode); + File open(const String &path, const char *mode); + + bool exists(const char *path); + bool exists(const String &path); + + Dir openDir(const char *path); + Dir openDir(const String &path); + + bool remove(const char *path); + bool remove(const String &path); + + bool rename(const char *pathFrom, const char *pathTo); + bool rename(const String &pathFrom, const String &pathTo); + + bool mkdir(const char *path); + bool mkdir(const String &path); + + bool rmdir(const char *path); + bool rmdir(const String &path); + + bool stat(const char *path, FSStat *st); + bool stat(const String &path, FSStat *st); + + // Low-level FS routines, not needed by most applications + bool gc(); + bool check(); + + time_t getCreationTime(); + + void setTimeCallback(time_t (*cb)(void)); + + friend class ::SDClass; // More of a frenemy, but SD needs internal implementation to get private FAT bits +protected: + FSImplPtr _impl; + FSImplPtr getImpl() { + return _impl; + } + time_t (*_timeCallback)(void) = nullptr; + static time_t _defaultTimeCB(void) { + return time(nullptr); + } +}; + +} // namespace fs + +extern "C" { +void close_all_fs(void); +void littlefs_request_end(void); +void spiffs_request_end(void); +} + +#ifndef FS_NO_GLOBALS +using fs::Dir; +using fs::File; +using fs::FS; +using fs::FSConfig; +using fs::FSInfo; +using fs::SeekCur; +using fs::SeekEnd; +using fs::SeekMode; +using fs::SeekSet; +#endif //FS_NO_GLOBALS diff --git a/examples/arduino_emulator/FSImpl.h b/examples/arduino_emulator/FSImpl.h new file mode 100644 index 00000000..940af062 --- /dev/null +++ b/examples/arduino_emulator/FSImpl.h @@ -0,0 +1,151 @@ +/* + FSImpl.h - base file system interface + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include +#include +#include + +namespace fs { + +class FileImpl { +public: + virtual ~FileImpl() {} + virtual size_t write(const uint8_t *buf, size_t size) = 0; + virtual int read(uint8_t *buf, size_t size) = 0; + virtual void flush() = 0; + virtual bool seek(uint32_t pos, SeekMode mode) = 0; + virtual size_t position() const = 0; + virtual size_t size() const = 0; + virtual int availableForWrite() { + return 0; + } + virtual bool truncate(uint32_t size) = 0; + virtual void close() = 0; + virtual const char *name() const = 0; + virtual const char *fullName() const = 0; + virtual bool isFile() const = 0; + virtual bool isDirectory() const = 0; + + // Filesystems *may* support a timestamp per-file, so allow the user to override with + // their own callback for *this specific* file (as opposed to the FSImpl call of the + // same name. The default implementation simply returns time(null) + virtual void setTimeCallback(time_t (*cb)(void)) { + _timeCallback = cb; + } + + // Return the last written time for a file. Undefined when called on a writable file + // as the FS is allowed to return either the time of the last write() operation or the + // time present in the filesystem metadata (often the last time the file was closed) + virtual time_t getLastWrite() { + return 0; // Default is to not support timestamps + } + // Same for creation time. + virtual time_t getCreationTime() { + return 0; // Default is to not support timestamps + } + +protected: + time_t (*_timeCallback)(void) = nullptr; +}; + +enum OpenMode { + OM_DEFAULT = 0, + OM_CREATE = 1, + OM_APPEND = 2, + OM_TRUNCATE = 4 +}; + +enum AccessMode { + AM_READ = 1, + AM_WRITE = 2, + AM_RW = AM_READ | AM_WRITE +}; + +class DirImpl { +public: + virtual ~DirImpl() {} + virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0; + virtual const char *fileName() = 0; + virtual size_t fileSize() = 0; + // Return the last written time for a file. Undefined when called on a writable file + // as the FS is allowed to return either the time of the last write() operation or the + // time present in the filesystem metadata (often the last time the file was closed) + virtual time_t fileTime() { + return 0; // By default, FS doesn't report file times + } + virtual time_t fileCreationTime() { + return 0; // By default, FS doesn't report file times + } + virtual bool isFile() const = 0; + virtual bool isDirectory() const = 0; + virtual bool next() = 0; + virtual bool rewind() = 0; + + // Filesystems *may* support a timestamp per-file, so allow the user to override with + // their own callback for *this specific* file (as opposed to the FSImpl call of the + // same name. The default implementation simply returns time(null) + virtual void setTimeCallback(time_t (*cb)(void)) { + _timeCallback = cb; + } + +protected: + time_t (*_timeCallback)(void) = nullptr; +}; + +class FSImpl { +public: + virtual ~FSImpl() {} + virtual bool setConfig(const FSConfig &cfg) = 0; + virtual bool begin() = 0; + virtual void end() = 0; + virtual bool format() = 0; + virtual bool info(FSInfo &info) = 0; + virtual FileImplPtr open(const char *path, OpenMode openMode, AccessMode accessMode) = 0; + virtual bool exists(const char *path) = 0; + virtual DirImplPtr openDir(const char *path) = 0; + virtual bool rename(const char *pathFrom, const char *pathTo) = 0; + virtual bool remove(const char *path) = 0; + virtual bool mkdir(const char *path) = 0; + virtual bool rmdir(const char *path) = 0; + virtual bool stat(const char *path, FSStat *st) = 0; + virtual bool gc() { + return true; // May not be implemented in all file systems. + } + virtual bool check() { + return true; // May not be implemented in all file systems. + } + virtual time_t getCreationTime() { + return 0; // May not be implemented in all file systems. + } + + // Filesystems *may* support a timestamp per-file, so allow the user to override with + // their own callback for all files on this FS. The default implementation simply + // returns the present time as reported by time(null) + virtual void setTimeCallback(time_t (*cb)(void)) { + _timeCallback = cb; + } + +protected: + time_t (*_timeCallback)(void) = nullptr; +}; + +} // namespace fs diff --git a/examples/arduino_emulator/README_FS.md b/examples/arduino_emulator/README_FS.md new file mode 100644 index 00000000..2311c3cf --- /dev/null +++ b/examples/arduino_emulator/README_FS.md @@ -0,0 +1,11 @@ +# FS class implementation + +FS.h, FS.cpp, and FSImpl.h are included here to satisfy +ESPAsyncWebServer's dependency on the FS class for serving static +file. Arduino-Emulator does not implement the FS class, which is a +ESP addition that is not part of the Arduino-Core API. It is present +in some but not all Arduino implementations on various architectures. + +The FS files herein are taken verbatim from the +earlephilhower/arduino-pico core, with the addition of one line in +FS.cpp to provide a null implementation of DEBUGV()