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()