// @lua-module fs FsLib // @lua-const MAX_READ_BYTES integer 65536 Largest portable whole-file or line // read. #include "../helpers.h" namespace esp32lua { namespace bindings { namespace { constexpr int32_t MAX_READ_BYTES = 65536; int32_t checkReadLimit(lua_State* state, int index) { const int32_t maxBytes = checkInt(state, index); luaL_argcheck(state, maxBytes >= 0 && maxBytes <= MAX_READ_BYTES, index, "exceeds fs.MAX_READ_BYTES"); return maxBytes; } int exists(lua_State* state) { const std::string path = checkString(state, 1); lua_pushboolean(state, Runtime::from(state)->fs().exists(path)); return 1; } int fileSize(lua_State* state) { const std::string path = checkString(state, 1); int32_t size = 0; const Status status = Runtime::from(state)->fs().fileSize(path, size); if (!status.ok) return pushError(state, status.error); lua_pushinteger(state, size); return 1; } int list(lua_State* state, bool directories) { const std::string path = checkString(state, 1); std::vector names; const FsProvider& fs = Runtime::from(state)->fs(); const Status status = directories ? fs.listDirs(path, names) : fs.listFiles(path, names); if (!status.ok) return pushError(state, status.error); pushStrings(state, names); return 1; } int listDirs(lua_State* state) { return list(state, true); } int listFiles(lua_State* state) { return list(state, false); } int mkdir(lua_State* state) { const std::string path = checkString(state, 1); return pushStatus(state, Runtime::from(state)->fs().mkdir(path)); } int readFile(lua_State* state) { const int32_t maxBytes = checkReadLimit(state, 2); const std::string path = checkString(state, 1); std::string content; const Status status = Runtime::from(state)->fs().readFile(path, maxBytes, content); if (!status.ok) return pushError(state, status.error); pushString(state, content); return 1; } int readLineAt(lua_State* state) { const int32_t offset = checkInt(state, 2); const int32_t maxBytes = checkReadLimit(state, 3); luaL_argcheck(state, offset >= 0, 2, "must not be negative"); const std::string path = checkString(state, 1); bool found = false; int32_t nextOffset = 0; std::string line; const Status status = Runtime::from(state)->fs().readLineAt( path, offset, maxBytes, found, line, nextOffset); if (!status.ok) { lua_pushnil(state); lua_pushnil(state); pushString(state, status.error); return 3; } if (!found) { lua_pushnil(state); return 1; } pushString(state, line); lua_pushinteger(state, nextOffset); return 2; } int remove(lua_State* state) { const std::string path = checkString(state, 1); return pushStatus(state, Runtime::from(state)->fs().remove(path)); } int removeTree(lua_State* state) { const std::string path = checkString(state, 1); return pushStatus(state, Runtime::from(state)->fs().removeTree(path)); } int rename(lua_State* state) { const std::string source = checkString(state, 1); const std::string destination = checkString(state, 2); return pushStatus(state, Runtime::from(state)->fs().rename(source, destination)); } int writeFile(lua_State* state) { const std::string path = checkString(state, 1); const std::string content = checkString(state, 2); return pushStatus(state, Runtime::from(state)->fs().writeFile(path, content)); } const luaL_Reg FUNCTIONS[] = { // --- Whether a path exists. // @param path string Absolute SD-card path; traversal components are // rejected. // @return boolean {"exists", exists}, // --- Returns the size of a file in bytes. // @param path string Absolute SD-card path. // @return integer|nil size // @return string|nil error Present when the path is missing or not a file. {"fileSize", fileSize}, // --- Lists the directories in a directory. // @param path string Absolute directory path. // @return string[]|nil names Sorted names, excluding hidden entries. // @return string|nil error {"listDirs", listDirs}, // --- Lists the files in a directory. // @param path string Absolute directory path. // @return string[]|nil names Sorted names, excluding hidden entries. // @return string|nil error {"listFiles", listFiles}, // --- Creates a directory. // @param path string Absolute directory path. // @return true|nil ok // @return string|nil error {"mkdir", mkdir}, // --- Reads a whole file. // @param path string Absolute file path. // @param maxBytes integer Maximum bytes to allocate, up to // fs.MAX_READ_BYTES; oversized files fail rather than truncate. // @return string|nil content // @return string|nil error {"readFile", readFile}, // --- Reads one line starting at a byte offset. // @param path string Absolute file path. // @param offset integer Zero-based byte offset; a mid-line offset advances // to the next line. // @param maxBytes integer Maximum line bytes to allocate, up to // fs.MAX_READ_BYTES. // @return string|nil line Nil at end of file or on failure. // @return integer|nil nextOffset Byte offset of the following line. // @return string|nil error Present when the file cannot be read or the line // exceeds maxBytes. {"readLineAt", readLineAt}, // --- Removes a file. // @param path string Absolute file path. Firmware-protected roots cannot be // removed. // @return true|nil ok // @return string|nil error {"remove", remove}, // --- Removes a directory and everything below it. // @param path string Absolute directory path. Firmware-protected roots // cannot be removed. // @return true|nil ok // @return string|nil error {"removeTree", removeTree}, // --- Renames a file or directory. // @param source string Absolute source path. // @param destination string Absolute destination path, which must not // exist. // @return true|nil ok // @return string|nil error {"rename", rename}, // --- Atomically replaces the destination or leaves its previous contents // intact. // @param path string Absolute file path. // @param content string // @return true|nil ok // @return string|nil error {"writeFile", writeFile}, {nullptr, nullptr}, }; } // namespace void registerFs(lua_State* state) { luaL_newlib(state, FUNCTIONS); lua_pushinteger(state, MAX_READ_BYTES); lua_setfield(state, -2, "MAX_READ_BYTES"); lua_setglobal(state, "fs"); } } // namespace bindings } // namespace esp32lua