Add object-based file API to dev-esp32 branch (#2524)
* #2521: Add object-based file API to dev-esp32 branch Merged in from master@c708828bbe853764b9de58fb8113a70f5a24002d minus some VFS changes and similar. * Updated file docs from master Omitting: * file.mount() * reference to rtctime.epoch2cal() * file.stat() As these are not available in dev-esp32 branch Unsure about whether "directories are not supported" is still true
This commit is contained in:
parent
7ded3960eb
commit
48b9518d76
|
@ -2,14 +2,29 @@
|
||||||
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "lauxlib.h"
|
#include "lauxlib.h"
|
||||||
|
#include "lmem.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
#include "vfs.h"
|
#include "vfs.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#define FILE_READ_CHUNK 1024
|
||||||
|
|
||||||
|
// use this time/date in absence of a timestamp
|
||||||
|
#define FILE_TIMEDEF_YEAR 1970
|
||||||
|
#define FILE_TIMEDEF_MON 01
|
||||||
|
#define FILE_TIMEDEF_DAY 01
|
||||||
|
#define FILE_TIMEDEF_HOUR 00
|
||||||
|
#define FILE_TIMEDEF_MIN 00
|
||||||
|
#define FILE_TIMEDEF_SEC 00
|
||||||
|
|
||||||
static int file_fd = 0;
|
static int file_fd = 0;
|
||||||
|
static int file_fd_ref = LUA_NOREF;
|
||||||
static int rtc_cb_ref = LUA_NOREF;
|
static int rtc_cb_ref = LUA_NOREF;
|
||||||
|
|
||||||
|
typedef struct _file_fd_ud {
|
||||||
|
int fd;
|
||||||
|
} file_fd_ud;
|
||||||
|
|
||||||
static void table2tm( lua_State *L, vfs_time *tm )
|
static void table2tm( lua_State *L, vfs_time *tm )
|
||||||
{
|
{
|
||||||
|
@ -23,12 +38,12 @@ static void table2tm( lua_State *L, vfs_time *tm )
|
||||||
lua_getfield( L, idx, "min" );
|
lua_getfield( L, idx, "min" );
|
||||||
lua_getfield( L, idx, "sec" );
|
lua_getfield( L, idx, "sec" );
|
||||||
|
|
||||||
tm->year = luaL_optint( L, ++idx, 2016 );
|
tm->year = luaL_optint( L, ++idx, FILE_TIMEDEF_YEAR );
|
||||||
tm->mon = luaL_optint( L, ++idx, 6 );
|
tm->mon = luaL_optint( L, ++idx, FILE_TIMEDEF_MON );
|
||||||
tm->day = luaL_optint( L, ++idx, 21 );
|
tm->day = luaL_optint( L, ++idx, FILE_TIMEDEF_DAY );
|
||||||
tm->hour = luaL_optint( L, ++idx, 0 );
|
tm->hour = luaL_optint( L, ++idx, FILE_TIMEDEF_HOUR );
|
||||||
tm->min = luaL_optint( L, ++idx, 0 );
|
tm->min = luaL_optint( L, ++idx, FILE_TIMEDEF_MIN );
|
||||||
tm->sec = luaL_optint( L, ++idx, 0 );
|
tm->sec = luaL_optint( L, ++idx, FILE_TIMEDEF_SEC );
|
||||||
|
|
||||||
// remove items from stack
|
// remove items from stack
|
||||||
lua_pop( L, 6 );
|
lua_pop( L, 6 );
|
||||||
|
@ -90,13 +105,47 @@ static int file_on(lua_State *L)
|
||||||
// Lua: close()
|
// Lua: close()
|
||||||
static int file_close( lua_State* L )
|
static int file_close( lua_State* L )
|
||||||
{
|
{
|
||||||
if(file_fd){
|
file_fd_ud *ud;
|
||||||
vfs_close(file_fd);
|
|
||||||
file_fd = 0;
|
if (lua_type( L, 1 ) != LUA_TUSERDATA) {
|
||||||
|
// fall back to last opened file
|
||||||
|
if (file_fd_ref != LUA_NOREF) {
|
||||||
|
lua_rawgeti( L, LUA_REGISTRYINDEX, file_fd_ref );
|
||||||
|
// top of stack is now default file descriptor
|
||||||
|
ud = (file_fd_ud *)luaL_checkudata(L, -1, "file.obj");
|
||||||
|
lua_pop( L, 1 );
|
||||||
|
} else {
|
||||||
|
// no default file currently opened
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
|
||||||
|
}
|
||||||
|
|
||||||
|
// unref default file descriptor
|
||||||
|
luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
|
||||||
|
file_fd_ref = LUA_NOREF;
|
||||||
|
|
||||||
|
if(ud->fd){
|
||||||
|
vfs_close(ud->fd);
|
||||||
|
// mark as closed
|
||||||
|
ud->fd = 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int file_obj_free( lua_State *L )
|
||||||
|
{
|
||||||
|
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
|
||||||
|
if (ud->fd) {
|
||||||
|
// close file if it's still open
|
||||||
|
vfs_close(ud->fd);
|
||||||
|
ud->fd = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_BUILD_SPIFFS
|
#ifdef CONFIG_BUILD_SPIFFS
|
||||||
// Lua: format()
|
// Lua: format()
|
||||||
static int file_format( lua_State* L )
|
static int file_format( lua_State* L )
|
||||||
|
@ -130,10 +179,10 @@ static int file_fscfg (lua_State *L)
|
||||||
static int file_open( lua_State* L )
|
static int file_open( lua_State* L )
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
if(file_fd){
|
|
||||||
vfs_close(file_fd);
|
// unref last file descriptor to allow gc'ing if not kept by user script
|
||||||
file_fd = 0;
|
luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
|
||||||
}
|
file_fd_ref = LUA_NOREF;
|
||||||
|
|
||||||
const char *fname = luaL_checklstring( L, 1, &len );
|
const char *fname = luaL_checklstring( L, 1, &len );
|
||||||
const char *basename = vfs_basename( fname );
|
const char *basename = vfs_basename( fname );
|
||||||
|
@ -146,7 +195,14 @@ static int file_open( lua_State* L )
|
||||||
if(!file_fd){
|
if(!file_fd){
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
} else {
|
} else {
|
||||||
lua_pushboolean(L, 1);
|
file_fd_ud *ud = (file_fd_ud *) lua_newuserdata( L, sizeof( file_fd_ud ) );
|
||||||
|
ud->fd = file_fd;
|
||||||
|
luaL_getmetatable( L, "file.obj" );
|
||||||
|
lua_setmetatable( L, -2 );
|
||||||
|
|
||||||
|
// store reference to opened file
|
||||||
|
lua_pushvalue( L, -1 );
|
||||||
|
file_fd_ref = luaL_ref( L, LUA_REGISTRYINDEX );
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -170,19 +226,36 @@ static int file_list( lua_State* L )
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int file_seek (lua_State *L)
|
static int get_file_obj( lua_State *L, int *argpos )
|
||||||
{
|
{
|
||||||
|
if (lua_type( L, 1 ) == LUA_TUSERDATA) {
|
||||||
|
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
|
||||||
|
*argpos = 2;
|
||||||
|
return ud->fd;
|
||||||
|
} else {
|
||||||
|
*argpos = 1;
|
||||||
|
return file_fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GET_FILE_OBJ int argpos; \
|
||||||
|
int fd = get_file_obj( L, &argpos );
|
||||||
|
|
||||||
|
static int file_seek (lua_State *L)
|
||||||
|
{
|
||||||
|
GET_FILE_OBJ;
|
||||||
|
|
||||||
static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END};
|
static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END};
|
||||||
static const char *const modenames[] = {"set", "cur", "end", NULL};
|
static const char *const modenames[] = {"set", "cur", "end", NULL};
|
||||||
if(!file_fd)
|
if(!fd)
|
||||||
return luaL_error(L, "open a file first");
|
return luaL_error(L, "open a file first");
|
||||||
int op = luaL_checkoption(L, 1, "cur", modenames);
|
int op = luaL_checkoption(L, argpos, "cur", modenames);
|
||||||
long offset = luaL_optlong(L, 2, 0);
|
long offset = luaL_optlong(L, ++argpos, 0);
|
||||||
op = vfs_lseek(file_fd, offset, mode[op]);
|
op = vfs_lseek(fd, offset, mode[op]);
|
||||||
if (op < 0)
|
if (op < 0)
|
||||||
lua_pushnil(L); /* error */
|
lua_pushnil(L); /* error */
|
||||||
else
|
else
|
||||||
lua_pushinteger(L, vfs_tell(file_fd));
|
lua_pushinteger(L, vfs_tell(fd));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +283,6 @@ static int file_remove( lua_State* L )
|
||||||
const char *fname = luaL_checklstring( L, 1, &len );
|
const char *fname = luaL_checklstring( L, 1, &len );
|
||||||
const char *basename = vfs_basename( fname );
|
const char *basename = vfs_basename( fname );
|
||||||
luaL_argcheck(L, strlen(basename) <= CONFIG_FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid");
|
luaL_argcheck(L, strlen(basename) <= CONFIG_FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid");
|
||||||
file_close(L);
|
|
||||||
vfs_remove((char *)fname);
|
vfs_remove((char *)fname);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -218,9 +290,11 @@ static int file_remove( lua_State* L )
|
||||||
// Lua: flush()
|
// Lua: flush()
|
||||||
static int file_flush( lua_State* L )
|
static int file_flush( lua_State* L )
|
||||||
{
|
{
|
||||||
if(!file_fd)
|
GET_FILE_OBJ;
|
||||||
|
|
||||||
|
if(!fd)
|
||||||
return luaL_error(L, "open a file first");
|
return luaL_error(L, "open a file first");
|
||||||
if(vfs_flush(file_fd) == 0)
|
if(vfs_flush(fd) == 0)
|
||||||
lua_pushboolean(L, 1);
|
lua_pushboolean(L, 1);
|
||||||
else
|
else
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
|
@ -231,10 +305,6 @@ static int file_flush( lua_State* L )
|
||||||
static int file_rename( lua_State* L )
|
static int file_rename( lua_State* L )
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
if(file_fd){
|
|
||||||
vfs_close(file_fd);
|
|
||||||
file_fd = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *oldname = luaL_checklstring( L, 1, &len );
|
const char *oldname = luaL_checklstring( L, 1, &len );
|
||||||
const char *basename = vfs_basename( oldname );
|
const char *basename = vfs_basename( oldname );
|
||||||
|
@ -253,38 +323,65 @@ static int file_rename( lua_State* L )
|
||||||
}
|
}
|
||||||
|
|
||||||
// g_read()
|
// g_read()
|
||||||
static int file_g_read( lua_State* L, int n, int16_t end_char )
|
static int file_g_read( lua_State* L, int n, int16_t end_char, int fd )
|
||||||
{
|
{
|
||||||
if(n <= 0 || n > LUAL_BUFFERSIZE)
|
static char *heap_mem = NULL;
|
||||||
n = LUAL_BUFFERSIZE;
|
// free leftover memory
|
||||||
if(end_char < 0 || end_char >255)
|
if (heap_mem) {
|
||||||
end_char = EOF;
|
luaM_free(L, heap_mem);
|
||||||
|
heap_mem = NULL;
|
||||||
luaL_Buffer b;
|
|
||||||
if(!file_fd)
|
|
||||||
return luaL_error(L, "open a file first");
|
|
||||||
|
|
||||||
luaL_buffinit(L, &b);
|
|
||||||
char *p = luaL_prepbuffer(&b);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
n = vfs_read(file_fd, p, n);
|
|
||||||
for (i = 0; i < n; ++i)
|
|
||||||
if (p[i] == end_char)
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i==0){
|
|
||||||
luaL_pushresult(&b); /* close buffer */
|
|
||||||
return (lua_objlen(L, -1) > 0); /* check whether read something */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vfs_lseek(file_fd, -(n - i), VFS_SEEK_CUR);
|
if(n <= 0)
|
||||||
luaL_addsize(&b, i);
|
n = FILE_READ_CHUNK;
|
||||||
luaL_pushresult(&b); /* close buffer */
|
|
||||||
return 1; /* read at least an `eol' */
|
if(end_char < 0 || end_char >255)
|
||||||
|
end_char = EOF;
|
||||||
|
|
||||||
|
|
||||||
|
if(!fd)
|
||||||
|
return luaL_error(L, "open a file first");
|
||||||
|
|
||||||
|
char *p;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (n > LUAL_BUFFERSIZE) {
|
||||||
|
// get buffer from heap
|
||||||
|
p = heap_mem = luaM_malloc(L, n);
|
||||||
|
} else {
|
||||||
|
// small chunks go onto the stack
|
||||||
|
p = alloca(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = vfs_read(fd, p, n);
|
||||||
|
// bypass search if no end character provided
|
||||||
|
if (n > 0 && end_char != EOF) {
|
||||||
|
for (i = 0; i < n; ++i)
|
||||||
|
if (p[i] == end_char)
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0 || n == VFS_RES_ERR) {
|
||||||
|
if (heap_mem) {
|
||||||
|
luaM_free(L, heap_mem);
|
||||||
|
heap_mem = NULL;
|
||||||
|
}
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_lseek(fd, -(n - i), VFS_SEEK_CUR);
|
||||||
|
lua_pushlstring(L, p, i);
|
||||||
|
if (heap_mem) {
|
||||||
|
luaM_free(L, heap_mem);
|
||||||
|
heap_mem = NULL;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lua: read()
|
// Lua: read()
|
||||||
|
@ -293,42 +390,46 @@ static int file_g_read( lua_State* L, int n, int16_t end_char )
|
||||||
// file.read('q') will read until 'q' or EOF is reached.
|
// file.read('q') will read until 'q' or EOF is reached.
|
||||||
static int file_read( lua_State* L )
|
static int file_read( lua_State* L )
|
||||||
{
|
{
|
||||||
unsigned need_len = LUAL_BUFFERSIZE;
|
unsigned need_len = FILE_READ_CHUNK;
|
||||||
int16_t end_char = EOF;
|
int16_t end_char = EOF;
|
||||||
size_t el;
|
size_t el;
|
||||||
if( lua_type( L, 1 ) == LUA_TNUMBER )
|
|
||||||
|
GET_FILE_OBJ;
|
||||||
|
|
||||||
|
if( lua_type( L, argpos ) == LUA_TNUMBER )
|
||||||
{
|
{
|
||||||
need_len = ( unsigned )luaL_checkinteger( L, 1 );
|
need_len = ( unsigned )luaL_checkinteger( L, argpos );
|
||||||
if( need_len > LUAL_BUFFERSIZE ){
|
|
||||||
need_len = LUAL_BUFFERSIZE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(lua_isstring(L, 1))
|
else if(lua_isstring(L, argpos))
|
||||||
{
|
{
|
||||||
const char *end = luaL_checklstring( L, 1, &el );
|
const char *end = luaL_checklstring( L, argpos, &el );
|
||||||
if(el!=1){
|
if(el!=1){
|
||||||
return luaL_error( L, "wrong arg range" );
|
return luaL_error( L, "wrong arg range" );
|
||||||
}
|
}
|
||||||
end_char = (int16_t)end[0];
|
end_char = (int16_t)end[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return file_g_read(L, need_len, end_char);
|
return file_g_read(L, need_len, end_char, fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lua: readline()
|
// Lua: readline()
|
||||||
static int file_readline( lua_State* L )
|
static int file_readline( lua_State* L )
|
||||||
{
|
{
|
||||||
return file_g_read(L, LUAL_BUFFERSIZE, '\n');
|
GET_FILE_OBJ;
|
||||||
|
|
||||||
|
return file_g_read(L, FILE_READ_CHUNK, '\n', fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lua: write("string")
|
// Lua: write("string")
|
||||||
static int file_write( lua_State* L )
|
static int file_write( lua_State* L )
|
||||||
{
|
{
|
||||||
if(!file_fd)
|
GET_FILE_OBJ;
|
||||||
|
|
||||||
|
if(!fd)
|
||||||
return luaL_error(L, "open a file first");
|
return luaL_error(L, "open a file first");
|
||||||
size_t l, rl;
|
size_t l, rl;
|
||||||
const char *s = luaL_checklstring(L, 1, &l);
|
const char *s = luaL_checklstring(L, argpos, &l);
|
||||||
rl = vfs_write(file_fd, s, l);
|
rl = vfs_write(fd, s, l);
|
||||||
if(rl==l)
|
if(rl==l)
|
||||||
lua_pushboolean(L, 1);
|
lua_pushboolean(L, 1);
|
||||||
else
|
else
|
||||||
|
@ -339,13 +440,15 @@ static int file_write( lua_State* L )
|
||||||
// Lua: writeline("string")
|
// Lua: writeline("string")
|
||||||
static int file_writeline( lua_State* L )
|
static int file_writeline( lua_State* L )
|
||||||
{
|
{
|
||||||
if(!file_fd)
|
GET_FILE_OBJ;
|
||||||
|
|
||||||
|
if(!fd)
|
||||||
return luaL_error(L, "open a file first");
|
return luaL_error(L, "open a file first");
|
||||||
size_t l, rl;
|
size_t l, rl;
|
||||||
const char *s = luaL_checklstring(L, 1, &l);
|
const char *s = luaL_checklstring(L, argpos, &l);
|
||||||
rl = vfs_write(file_fd, s, l);
|
rl = vfs_write(fd, s, l);
|
||||||
if(rl==l){
|
if(rl==l){
|
||||||
rl = vfs_write(file_fd, "\n", 1);
|
rl = vfs_write(fd, "\n", 1);
|
||||||
if(rl==1)
|
if(rl==1)
|
||||||
lua_pushboolean(L, 1);
|
lua_pushboolean(L, 1);
|
||||||
else
|
else
|
||||||
|
@ -390,6 +493,20 @@ static int file_chdir( lua_State *L )
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const LUA_REG_TYPE file_obj_map[] =
|
||||||
|
{
|
||||||
|
{ LSTRKEY( "close" ), LFUNCVAL( file_close ) },
|
||||||
|
{ LSTRKEY( "read" ), LFUNCVAL( file_read ) },
|
||||||
|
{ LSTRKEY( "readline" ), LFUNCVAL( file_readline ) },
|
||||||
|
{ LSTRKEY( "write" ), LFUNCVAL( file_write ) },
|
||||||
|
{ LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) },
|
||||||
|
{ LSTRKEY( "seek" ), LFUNCVAL( file_seek ) },
|
||||||
|
{ LSTRKEY( "flush" ), LFUNCVAL( file_flush ) },
|
||||||
|
{ LSTRKEY( "__gc" ), LFUNCVAL( file_obj_free ) },
|
||||||
|
{ LSTRKEY( "__index" ), LROVAL( file_obj_map ) },
|
||||||
|
{ LNILKEY, LNILVAL }
|
||||||
|
};
|
||||||
|
|
||||||
// Module function map
|
// Module function map
|
||||||
static const LUA_REG_TYPE file_map[] = {
|
static const LUA_REG_TYPE file_map[] = {
|
||||||
{ LSTRKEY( "list" ), LFUNCVAL( file_list ) },
|
{ LSTRKEY( "list" ), LFUNCVAL( file_list ) },
|
||||||
|
@ -416,4 +533,9 @@ static const LUA_REG_TYPE file_map[] = {
|
||||||
{ LNILKEY, LNILVAL }
|
{ LNILKEY, LNILVAL }
|
||||||
};
|
};
|
||||||
|
|
||||||
NODEMCU_MODULE(FILE, "file", file_map, NULL);
|
int luaopen_file( lua_State *L ) {
|
||||||
|
luaL_rometatable( L, "file.obj", (void *)file_obj_map );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
NODEMCU_MODULE(FILE, "file", file_map, luaopen_file);
|
||||||
|
|
|
@ -7,9 +7,7 @@ The file module provides access to the file system and its individual files.
|
||||||
|
|
||||||
The file system is a flat file system, with no notion of subdirectories/folders.
|
The file system is a flat file system, with no notion of subdirectories/folders.
|
||||||
|
|
||||||
Only one file can be open at any given time.
|
Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card if [FatFS is enabled](../sdcard.md).
|
||||||
|
|
||||||
Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card is [FatFS is enabled](../sdcard.md).
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- open file in flash:
|
-- open file in flash:
|
||||||
|
@ -34,6 +32,10 @@ Change current directory (and drive). This will be used when no drive/directory
|
||||||
|
|
||||||
Current directory defaults to the root of internal SPIFFS (`/FLASH`) after system start.
|
Current directory defaults to the root of internal SPIFFS (`/FLASH`) after system start.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
Function is only available when [FatFS support](../sdcard.md#enabling-fatfs) is compiled into the firmware.
|
||||||
|
|
||||||
#### Syntax
|
#### Syntax
|
||||||
`file.chdir(dir)`
|
`file.chdir(dir)`
|
||||||
|
|
||||||
|
@ -43,30 +45,6 @@ Current directory defaults to the root of internal SPIFFS (`/FLASH`) after syste
|
||||||
#### Returns
|
#### Returns
|
||||||
`true` on success, `false` otherwise
|
`true` on success, `false` otherwise
|
||||||
|
|
||||||
## file.close()
|
|
||||||
|
|
||||||
Closes the open file, if any.
|
|
||||||
|
|
||||||
#### Syntax
|
|
||||||
`file.close()`
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
none
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
`nil`
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
```lua
|
|
||||||
-- open 'init.lua', print the first line.
|
|
||||||
if file.open("init.lua", "r") then
|
|
||||||
print(file.readline())
|
|
||||||
file.close()
|
|
||||||
end
|
|
||||||
```
|
|
||||||
#### See also
|
|
||||||
[`file.open()`](#fileopen)
|
|
||||||
|
|
||||||
## file.exists()
|
## file.exists()
|
||||||
|
|
||||||
Determines whether the specified file exists.
|
Determines whether the specified file exists.
|
||||||
|
@ -95,39 +73,13 @@ end
|
||||||
#### See also
|
#### See also
|
||||||
[`file.list()`](#filelist)
|
[`file.list()`](#filelist)
|
||||||
|
|
||||||
## file.flush()
|
|
||||||
|
|
||||||
Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()`](#fileclose) performs an implicit flush as well.
|
|
||||||
|
|
||||||
#### Syntax
|
|
||||||
`file.flush()`
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
none
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
`nil`
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
```lua
|
|
||||||
-- open 'init.lua' in 'a+' mode
|
|
||||||
if file.open("init.lua", "a+") then
|
|
||||||
-- write 'foo bar' to the end of the file
|
|
||||||
file.write('foo bar')
|
|
||||||
file.flush()
|
|
||||||
-- write 'baz' too
|
|
||||||
file.write('baz')
|
|
||||||
file.close()
|
|
||||||
end
|
|
||||||
```
|
|
||||||
#### See also
|
|
||||||
[`file.close()`](#fileclose)
|
|
||||||
|
|
||||||
## file.format()
|
## file.format()
|
||||||
|
|
||||||
Format the file system. Completely erases any existing file system and writes a new one. Depending on the size of the flash chip in the ESP, this may take several seconds.
|
Format the file system. Completely erases any existing file system and writes a new one. Depending on the size of the flash chip in the ESP, this may take several seconds.
|
||||||
|
|
||||||
Not supported for SD cards.
|
!!! note
|
||||||
|
|
||||||
|
Function is not supported for SD cards.
|
||||||
|
|
||||||
#### Syntax
|
#### Syntax
|
||||||
`file.format()`
|
`file.format()`
|
||||||
|
@ -145,7 +97,9 @@ none
|
||||||
|
|
||||||
Returns the flash address and physical size of the file system area, in bytes.
|
Returns the flash address and physical size of the file system area, in bytes.
|
||||||
|
|
||||||
Not supported for SD cards.
|
!!! note
|
||||||
|
|
||||||
|
Function is not supported for SD cards.
|
||||||
|
|
||||||
#### Syntax
|
#### Syntax
|
||||||
`file.fscfg()`
|
`file.fscfg()`
|
||||||
|
@ -211,6 +165,7 @@ end
|
||||||
Registers callback functions.
|
Registers callback functions.
|
||||||
|
|
||||||
Trigger events are:
|
Trigger events are:
|
||||||
|
|
||||||
- `rtc` deliver current date & time to the file system. Function is expected to return a table containing the fields `year`, `mon`, `day`, `hour`, `min`, `sec` of current date and time. Not supported for internal flash.
|
- `rtc` deliver current date & time to the file system. Function is expected to return a table containing the fields `year`, `mon`, `day`, `hour`, `min`, `sec` of current date and time. Not supported for internal flash.
|
||||||
|
|
||||||
#### Syntax
|
#### Syntax
|
||||||
|
@ -255,7 +210,7 @@ When done with the file, it must be closed using `file.close()`.
|
||||||
- "a+": append update mode, previous data is preserved, writing is only allowed at the end of file
|
- "a+": append update mode, previous data is preserved, writing is only allowed at the end of file
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
`nil` if file not opened, or not exists (read modes). `true` if file opened ok.
|
file object if file opened ok. `nil` if file not opened, or not exists (read modes).
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
```lua
|
```lua
|
||||||
|
@ -265,70 +220,19 @@ if file.open("init.lua", "r") then
|
||||||
file.close()
|
file.close()
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
#### See also
|
#### Example (object model)
|
||||||
- [`file.close()`](#fileclose)
|
|
||||||
- [`file.readline()`](#filereadline)
|
|
||||||
|
|
||||||
## file.read()
|
|
||||||
|
|
||||||
Read content from the open file.
|
|
||||||
|
|
||||||
#### Syntax
|
|
||||||
`file.read([n_or_str])`
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
- `n_or_str`:
|
|
||||||
- if nothing passed in, read up to `LUAL_BUFFERSIZE` bytes (default 1024) or the entire file (whichever is smaller)
|
|
||||||
- if passed a number n, then read the file until the lesser of `n` bytes, `LUAL_BUFFERSIZE` bytes, or EOF is reached. Specifying a number larger than the buffer size will read the buffer size.
|
|
||||||
- if passed a string `str`, then read until `str` appears next in the file, `LUAL_BUFFERSIZE` bytes have been read, or EOF is reached
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
File content as a string, or nil when EOF
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
```lua
|
```lua
|
||||||
-- print the first line of 'init.lua'
|
-- open 'init.lua', print the first line.
|
||||||
if file.open("init.lua", "r") then
|
fd = file.open("init.lua", "r")
|
||||||
print(file.read('\n'))
|
if fd then
|
||||||
file.close()
|
print(fd:readline())
|
||||||
end
|
fd:close(); fd = nil
|
||||||
|
|
||||||
-- print the first 5 bytes of 'init.lua'
|
|
||||||
if file.open("init.lua", "r") then
|
|
||||||
print(file.read(5))
|
|
||||||
file.close()
|
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
#### See also
|
#### See also
|
||||||
- [`file.open()`](#fileopen)
|
- [`file.close()`](#fileclose-fileobjclose)
|
||||||
- [`file.readline()`](#filereadline)
|
- [`file.readline()`](#filereadline-fileobjreadline)
|
||||||
|
|
||||||
## file.readline()
|
|
||||||
|
|
||||||
Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than `LUAL_BUFFERSIZE`, this function only returns the first `LUAL_BUFFERSIZE` bytes (this is 1024 bytes by default).
|
|
||||||
|
|
||||||
#### Syntax
|
|
||||||
`file.readline()`
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
none
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
```lua
|
|
||||||
-- print the first line of 'init.lua'
|
|
||||||
if file.open("init.lua", "r") then
|
|
||||||
print(file.readline())
|
|
||||||
file.close()
|
|
||||||
end
|
|
||||||
```
|
|
||||||
#### See also
|
|
||||||
- [`file.open()`](#fileopen)
|
|
||||||
- [`file.close()`](#fileclose)
|
|
||||||
- [`file.read()`](#filereade)
|
|
||||||
|
|
||||||
## file.remove()
|
## file.remove()
|
||||||
|
|
||||||
|
@ -373,12 +277,183 @@ Renames a file. If a file is currently open, it will be closed first.
|
||||||
file.rename("temp.lua","init.lua")
|
file.rename("temp.lua","init.lua")
|
||||||
```
|
```
|
||||||
|
|
||||||
## file.seek()
|
# File access functions
|
||||||
|
|
||||||
|
The `file` module provides several functions to access the content of a file after it has been opened with [`file.open()`](#fileopen). They can be used as part of a basic model or an object model:
|
||||||
|
|
||||||
|
## Basic model
|
||||||
|
In the basic model there is max one file opened at a time. The file access functions operate on this file per default. If another file is opened, the previous default file needs to be closed beforehand.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- open 'init.lua', print the first line.
|
||||||
|
if file.open("init.lua", "r") then
|
||||||
|
print(file.readline())
|
||||||
|
file.close()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Object model
|
||||||
|
Files are represented by file objects which are created by `file.open()`. File access functions are available as methods of this object, and multiple file objects can coexist.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
src = file.open("init.lua", "r")
|
||||||
|
if src then
|
||||||
|
dest = file.open("copy.lua", "w")
|
||||||
|
if dest then
|
||||||
|
local line
|
||||||
|
repeat
|
||||||
|
line = src:read()
|
||||||
|
if line then
|
||||||
|
dest:write(line)
|
||||||
|
end
|
||||||
|
until line == nil
|
||||||
|
dest:close(); dest = nil
|
||||||
|
end
|
||||||
|
src:close(); dest = nil
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! Attention
|
||||||
|
|
||||||
|
It is recommended to use only one single model within the application. Concurrent use of both models can yield unpredictable behavior: Closing the default file from basic model will also close the correspoding file object. Closing a file from object model will also close the default file if they are the same file.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
|
||||||
|
The maximum number of open files on SPIFFS is determined at compile time by `SPIFFS_MAX_OPEN_FILES` in `user_config.h`.
|
||||||
|
|
||||||
|
## file.close(), file.obj:close()
|
||||||
|
|
||||||
|
Closes the open file, if any.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`file.close()`
|
||||||
|
|
||||||
|
`fd:close()`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
none
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
#### See also
|
||||||
|
[`file.open()`](#fileopen)
|
||||||
|
|
||||||
|
## file.flush(), file.obj:flush()
|
||||||
|
|
||||||
|
Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()` / `fd:close()`](#fileclose-fileobjclose) performs an implicit flush as well.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`file.flush()`
|
||||||
|
|
||||||
|
`fd:flush()`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
none
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
#### Example (basic model)
|
||||||
|
```lua
|
||||||
|
-- open 'init.lua' in 'a+' mode
|
||||||
|
if file.open("init.lua", "a+") then
|
||||||
|
-- write 'foo bar' to the end of the file
|
||||||
|
file.write('foo bar')
|
||||||
|
file.flush()
|
||||||
|
-- write 'baz' too
|
||||||
|
file.write('baz')
|
||||||
|
file.close()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
#### See also
|
||||||
|
[`file.close()` / `file.obj:close()`](#fileclose-fileobjclose)
|
||||||
|
|
||||||
|
## file.read(), file.obj:read()
|
||||||
|
|
||||||
|
Read content from the open file.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
The function temporarily allocates 2 * (number of requested bytes) on the heap for buffering and processing the read data. Default chunk size (`FILE_READ_CHUNK`) is 1024 bytes and is regarded to be safe. Pushing this by 4x or more can cause heap overflows depending on the application. Consider this when selecting a value for parameter `n_or_char`.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`file.read([n_or_char])`
|
||||||
|
|
||||||
|
`fd:read([n_or_char])`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- `n_or_char`:
|
||||||
|
- if nothing passed in, then read up to `FILE_READ_CHUNK` bytes or the entire file (whichever is smaller).
|
||||||
|
- if passed a number `n`, then read up to `n` bytes or the entire file (whichever is smaller).
|
||||||
|
- if passed a string containing the single character `char`, then read until `char` appears next in the file, `FILE_READ_CHUNK` bytes have been read, or EOF is reached.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
File content as a string, or nil when EOF
|
||||||
|
|
||||||
|
#### Example (basic model)
|
||||||
|
```lua
|
||||||
|
-- print the first line of 'init.lua'
|
||||||
|
if file.open("init.lua", "r") then
|
||||||
|
print(file.read('\n'))
|
||||||
|
file.close()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example (object model)
|
||||||
|
```lua
|
||||||
|
-- print the first 5 bytes of 'init.lua'
|
||||||
|
fd = file.open("init.lua", "r")
|
||||||
|
if fd then
|
||||||
|
print(fd:read(5))
|
||||||
|
fd:close(); fd = nil
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
#### See also
|
||||||
|
- [`file.open()`](#fileopen)
|
||||||
|
- [`file.readline()` / `file.obj:readline()`](#filereadline-fileobjreadline)
|
||||||
|
|
||||||
|
## file.readline(), file.obj:readline()
|
||||||
|
|
||||||
|
Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than 1024, this function only returns the first 1024 bytes.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`file.readline()`
|
||||||
|
|
||||||
|
`fd:readline()`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
none
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
|
||||||
|
|
||||||
|
#### Example (basic model)
|
||||||
|
```lua
|
||||||
|
-- print the first line of 'init.lua'
|
||||||
|
if file.open("init.lua", "r") then
|
||||||
|
print(file.readline())
|
||||||
|
file.close()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
#### See also
|
||||||
|
- [`file.open()`](#fileopen)
|
||||||
|
- [`file.close()` / `file.obj:close()`](#fileclose-fileobjclose)
|
||||||
|
- [`file.read()` / `file.obj:read()`](#fileread-fileobjread)
|
||||||
|
|
||||||
|
|
||||||
|
## file.seek(), file.obj:seek()
|
||||||
|
|
||||||
Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence.
|
Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence.
|
||||||
|
|
||||||
#### Syntax
|
#### Syntax
|
||||||
`file.seek([whence [, offset]])`
|
`file.seek([whence [, offset]])`
|
||||||
|
|
||||||
|
`fd:seek([whence [, offset]])`
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
- `whence`
|
- `whence`
|
||||||
- "set": base is position 0 (beginning of the file)
|
- "set": base is position 0 (beginning of the file)
|
||||||
|
@ -391,7 +466,7 @@ If no parameters are given, the function simply returns the current file offset.
|
||||||
#### Returns
|
#### Returns
|
||||||
the resulting file position, or `nil` on error
|
the resulting file position, or `nil` on error
|
||||||
|
|
||||||
#### Example
|
#### Example (basic model)
|
||||||
```lua
|
```lua
|
||||||
if file.open("init.lua", "r") then
|
if file.open("init.lua", "r") then
|
||||||
-- skip the first 5 bytes of the file
|
-- skip the first 5 bytes of the file
|
||||||
|
@ -403,20 +478,22 @@ end
|
||||||
#### See also
|
#### See also
|
||||||
[`file.open()`](#fileopen)
|
[`file.open()`](#fileopen)
|
||||||
|
|
||||||
## file.write()
|
## file.write(), file.obj:write()
|
||||||
|
|
||||||
Write a string to the open file.
|
Write a string to the open file.
|
||||||
|
|
||||||
#### Syntax
|
#### Syntax
|
||||||
`file.write(string)`
|
`file.write(string)`
|
||||||
|
|
||||||
|
`fd:write(string)`
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
`string` content to be write to file
|
`string` content to be write to file
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
`true` if the write is ok, `nil` on error
|
`true` if the write is ok, `nil` on error
|
||||||
|
|
||||||
#### Example
|
#### Example (basic model)
|
||||||
```lua
|
```lua
|
||||||
-- open 'init.lua' in 'a+' mode
|
-- open 'init.lua' in 'a+' mode
|
||||||
if file.open("init.lua", "a+") then
|
if file.open("init.lua", "a+") then
|
||||||
|
@ -426,24 +503,37 @@ if file.open("init.lua", "a+") then
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Example (object model)
|
||||||
|
```lua
|
||||||
|
-- open 'init.lua' in 'a+' mode
|
||||||
|
fd = file.open("init.lua", "a+")
|
||||||
|
if fd then
|
||||||
|
-- write 'foo bar' to the end of the file
|
||||||
|
fd:write('foo bar')
|
||||||
|
fd:close()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
#### See also
|
#### See also
|
||||||
- [`file.open()`](#fileopen)
|
- [`file.open()`](#fileopen)
|
||||||
- [`file.writeline()`](#filewriteline)
|
- [`file.writeline()` / `file.obj:writeline()`](#filewriteline-fileobjwriteline)
|
||||||
|
|
||||||
## file.writeline()
|
## file.writeline(), file.obj:writeline()
|
||||||
|
|
||||||
Write a string to the open file and append '\n' at the end.
|
Write a string to the open file and append '\n' at the end.
|
||||||
|
|
||||||
#### Syntax
|
#### Syntax
|
||||||
`file.writeline(string)`
|
`file.writeline(string)`
|
||||||
|
|
||||||
|
`fd:writeline(string)`
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
`string` content to be write to file
|
`string` content to be write to file
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
`true` if write ok, `nil` on error
|
`true` if write ok, `nil` on error
|
||||||
|
|
||||||
#### Example
|
#### Example (basic model)
|
||||||
```lua
|
```lua
|
||||||
-- open 'init.lua' in 'a+' mode
|
-- open 'init.lua' in 'a+' mode
|
||||||
if file.open("init.lua", "a+") then
|
if file.open("init.lua", "a+") then
|
||||||
|
@ -455,4 +545,4 @@ end
|
||||||
|
|
||||||
#### See also
|
#### See also
|
||||||
- [`file.open()`](#fileopen)
|
- [`file.open()`](#fileopen)
|
||||||
- [`file.readline()`](#filereadline)
|
- [`file.readline()` / `file.obj:readline()`](#filereadline-fileobjreadline)
|
||||||
|
|
Loading…
Reference in New Issue