#include "luavgl.h" #include "private.h" typedef struct luavgl_fs_s { lv_fs_file_t file; bool closed; /* userdata exists but lv_fs has been closed */ } luavgl_fs_t; typedef struct luavgl_dir_s { lv_fs_dir_t dir; bool closed; /* userdata exists but lv_fs has been closed */ } luavgl_dir_t; static luavgl_fs_t *luavgl_to_fs(lua_State *L, int index) { luavgl_fs_t *v = luaL_checkudata(L, index, "lv_fs"); if (v->closed) { luaL_error(L, "attempt to use a closed file"); } return v; } static luavgl_dir_t *luavgl_to_dir(lua_State *L, int index) { luavgl_dir_t *v = luaL_checkudata(L, index, "lv_dir"); if (v->closed) { luaL_error(L, "attempt to use a closed file"); } return v; } /** * luavgl.open_file(filename, [mode]) * mode: "r" "w", others not supported. */ static int luavgl_fs_open(lua_State *L) { const char *path = lua_tostring(L, 1); const char *mode = "r"; if (lua_type(L, 2) == LUA_TSTRING) { mode = lua_tostring(L, 2); } luavgl_fs_t *f = lua_newuserdata(L, sizeof(luavgl_fs_t)); f->closed = false; lv_fs_mode_t lmode = 0; if (strchr(mode, 'r')) lmode |= LV_FS_MODE_RD; if (strchr(mode, 'w')) lmode |= LV_FS_MODE_WR; lv_fs_res_t res = lv_fs_open(&f->file, path, lmode); if (res != LV_FS_RES_OK) { debug("open failed: %s\n", path); lua_pushnil(L); lua_pushfstring(L, "open failed: %s\n", path); lua_pushinteger(L, res); /* return lv_fs error number */ return 3; } luaL_getmetatable(L, "lv_fs"); lua_setmetatable(L, -2); return 1; } /* read_chars modified for lv_fs */ static int read_chars(lua_State *L, lv_fs_file_t *f, size_t n) { uint32_t nr; /* number of chars actually read */ char *p; luaL_Buffer b; luaL_buffinit(L, &b); p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */ lv_fs_res_t res = lv_fs_read(f, p, n, &nr); if (res != LV_FS_RES_OK) return false; luaL_pushresultsize(&b, nr); return (nr > 0); /* true iff read something */ } /* read_all modified for lv_fs */ static void read_all(lua_State *L, lv_fs_file_t *f) { uint32_t nr; luaL_Buffer b; luaL_buffinit(L, &b); do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ char *p = luaL_prepbuffer(&b); lv_fs_read(f, p, LUAL_BUFFERSIZE, &nr); luaL_addsize(&b, nr); } while (nr == LUAL_BUFFERSIZE); luaL_pushresult(&b); /* close buffer */ } /** * f:read() */ static int luavgl_fs_read(lua_State *L) { luavgl_fs_t *f = luavgl_to_fs(L, 1); int nargs = lua_gettop(L) - 1; int n, success; if (nargs == 0) return luaL_error(L, "read mode required: 'a' or num "); /* ensure stack space for all results and for auxlib's buffer */ luaL_checkstack(L, nargs + LUA_MINSTACK, "too many arguments"); success = 1; for (n = 2; nargs-- && success; n++) { if (lua_type(L, n) == LUA_TNUMBER) { size_t l = (size_t)luaL_checkinteger(L, n); success = read_chars(L, &f->file, l); } else { const char *p = luaL_checkstring(L, n); if (*p == '*') p++; /* skip optional '*' (for compatibility) */ switch (*p) { case 'a': /* file */ read_all(L, &f->file); /* read entire file */ success = 1; /* always success */ break; case 'n': /* number */ case 'l': /* line */ case 'L': /* line with end-of-line */ default: return luaL_argerror(L, n, "invalid format"); } } } if (!success) { lua_pop(L, 1); /* remove last result */ lua_pushnil(L); /* push nil instead */ } return n - 2; } static int luavgl_fs_write(lua_State *L) { luavgl_fs_t *f = luavgl_to_fs(L, 1); lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ int arg = 2; int nargs = lua_gettop(L) - arg; int status = 1; lv_fs_res_t res = 0; size_t l; uint32_t nw; const char *s; for (; nargs--; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { s = luaL_tolstring(L, arg, &l); res = lv_fs_write(&f->file, s, l, &nw); lua_pop(L, 1); } else { s = luaL_checklstring(L, arg, &l); res = lv_fs_write(&f->file, s, l, &nw); } status = status && nw == l && res == LV_FS_RES_OK; } if (status) { return 1; /* file handle already on stack top */ } lua_pushnil(L); lua_pushstring(L, "failed"); /* no details */ lua_pushinteger(L, res); return 3; } static int luavgl_fs_close(lua_State *L) { luavgl_fs_t *f = luavgl_to_fs(L, 1); debug("\n"); f->closed = true; lv_fs_close(&f->file); return 0; } static int luavgl_fs_seek(lua_State *L) { static const int mode[] = {LV_FS_SEEK_SET, LV_FS_SEEK_CUR, LV_FS_SEEK_END}; static const char *const modenames[] = {"set", "cur", "end", NULL}; luavgl_fs_t *f = luavgl_to_fs(L, 1); int op = luaL_checkoption(L, 2, "cur", modenames); lua_Integer p3 = luaL_optinteger(L, 3, 0); uint32_t offset = (uint32_t)p3; luaL_argcheck(L, (lua_Integer)offset == p3, 3, "not an integer in proper range"); lv_fs_res_t res = lv_fs_seek(&f->file, offset, mode[op]); if (res != LV_FS_RES_OK) { lua_pushnil(L); lua_pushstring(L, "failed"); lua_pushinteger(L, res); return 3; } uint32_t pos; res = lv_fs_tell(&f->file, &pos); if (res != LV_FS_RES_OK) { lua_pushinteger(L, 0); } else { lua_pushinteger(L, (lua_Integer)pos); } return 1; } static int luavgl_fs_tostring(lua_State *L) { lua_pushfstring(L, "lv_fs handler: %p\n", luavgl_to_fs(L, 1)); return 1; } static int luavgl_fs_gc(lua_State *L) { debug("\n"); luavgl_fs_t *v = luaL_checkudata(L, 1, "lv_fs"); if (!v->closed) { v->closed = true; lv_fs_close(&v->file); } return 0; } /** * luavgl.open_dir(path) */ static int luavgl_dir_open(lua_State *L) { const char *path = lua_tostring(L, 1); luavgl_dir_t *d = lua_newuserdata(L, sizeof(luavgl_dir_t)); d->closed = false; lv_fs_res_t res = lv_fs_dir_open(&d->dir, path); if (res != LV_FS_RES_OK) { debug("open failed: %s\n", path); lua_pushnil(L); lua_pushfstring(L, "open failed: %s\n", path); lua_pushinteger(L, res); /* return lv_fs error number */ return 3; } luaL_getmetatable(L, "lv_dir"); lua_setmetatable(L, -2); return 1; } static int luavgl_dir_read(lua_State *L) { luavgl_dir_t *d = luavgl_to_dir(L, 1); char buffer[PATH_MAX]; lv_fs_res_t res = lv_fs_dir_read(&d->dir, buffer); if (res != LV_FS_RES_OK || buffer[0] == '\0') { lua_pushnil(L); } else { lua_pushstring(L, buffer); } return 1; } static int luavgl_dir_close(lua_State *L) { debug("\n"); luavgl_dir_t *d = luavgl_to_dir(L, 1); d->closed = true; lv_fs_dir_close(&d->dir); return 0; } static int luavgl_dir_tostring(lua_State *L) { lua_pushfstring(L, "lv_fs dir handler: %p\n", luavgl_to_dir(L, 1)); return 1; } static int luavgl_dir_gc(lua_State *L) { debug("\n"); luavgl_dir_t *v = luaL_checkudata(L, 1, "lv_dir"); if (!v->closed) { v->closed = true; lv_fs_dir_close(&v->dir); } return 0; } /** * luavgl.fs lib * */ static const luaL_Reg fs_lib[] = { {"open_file", luavgl_fs_open }, {"open_dir", luavgl_dir_open}, {NULL, NULL }, }; /* ** methods for file handles */ static const luaL_Reg fs_methods[] = { {"read", luavgl_fs_read }, {"write", luavgl_fs_write}, {"close", luavgl_fs_close}, {"seek", luavgl_fs_seek }, {NULL, NULL }, }; static const luaL_Reg fs_meta[] = { {"__gc", luavgl_fs_gc }, {"__close", luavgl_fs_gc }, {"__tostring", luavgl_fs_tostring}, {"__index", NULL }, /* place holder */ {NULL, NULL } }; /* ** methods for dir handles */ static const luaL_Reg dir_methods[] = { {"read", luavgl_dir_read }, {"close", luavgl_dir_close}, {NULL, NULL }, }; static const luaL_Reg dir_meta[] = { {"__gc", luavgl_dir_gc }, {"__close", luavgl_dir_gc }, {"__tostring", luavgl_dir_tostring}, {"__index", NULL }, /* place holder */ {NULL, NULL } }; static void luavgl_fs_init(lua_State *L) { /* create lv_fs metatable */ luaL_newmetatable(L, "lv_fs"); luaL_setfuncs(L, fs_meta, 0); luaL_newlib(L, fs_methods); lua_setfield(L, -2, "__index"); lua_pop(L, 1); /* pop metatable */ /* create lv_dir metatable */ luaL_newmetatable(L, "lv_dir"); luaL_setfuncs(L, dir_meta, 0); luaL_newlib(L, dir_methods); lua_setfield(L, -2, "__index"); lua_pop(L, 1); /* pop metatable */ /* luavgl.fs lib */ luaL_newlib(L, fs_lib); lua_setfield(L, -2, "fs"); }