diff --git a/app/modules/file.c b/app/modules/file.c index 3e9de55d..daa70cf2 100644 --- a/app/modules/file.c +++ b/app/modules/file.c @@ -13,6 +13,14 @@ #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_ref = LUA_NOREF; static int rtc_cb_ref = LUA_NOREF; @@ -33,12 +41,12 @@ static void table2tm( lua_State *L, vfs_time *tm ) lua_getfield( L, idx, "min" ); lua_getfield( L, idx, "sec" ); - tm->year = luaL_optint( L, ++idx, 2016 ); - tm->mon = luaL_optint( L, ++idx, 6 ); - tm->day = luaL_optint( L, ++idx, 21 ); - tm->hour = luaL_optint( L, ++idx, 0 ); - tm->min = luaL_optint( L, ++idx, 0 ); - tm->sec = luaL_optint( L, ++idx, 0 ); + tm->year = luaL_optint( L, ++idx, FILE_TIMEDEF_YEAR ); + tm->mon = luaL_optint( L, ++idx, FILE_TIMEDEF_MON ); + tm->day = luaL_optint( L, ++idx, FILE_TIMEDEF_DAY ); + tm->hour = luaL_optint( L, ++idx, FILE_TIMEDEF_HOUR ); + tm->min = luaL_optint( L, ++idx, FILE_TIMEDEF_MIN ); + tm->sec = luaL_optint( L, ++idx, FILE_TIMEDEF_SEC ); // remove items from stack lua_pop( L, 6 ); @@ -317,6 +325,72 @@ static int file_rename( lua_State* L ) return 1; } +// Lua: stat(filename) +static int file_stat( lua_State* L ) +{ + size_t len; + const char *fname = luaL_checklstring( L, 1, &len ); + luaL_argcheck( L, c_strlen(fname) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid" ); + + vfs_item *stat = vfs_stat( (char *)fname ); + + if (!stat) { + lua_pushnil( L ); + return 1; + } + + lua_createtable( L, 0, 7 ); + + lua_pushinteger( L, vfs_item_size( stat ) ); + lua_setfield( L, -2, "size" ); + + lua_pushstring( L, vfs_item_name( stat ) ); + lua_setfield( L, -2, "name" ); + + lua_pushboolean( L, vfs_item_is_dir( stat ) ); + lua_setfield( L, -2, "is_dir" ); + + lua_pushboolean( L, vfs_item_is_rdonly( stat ) ); + lua_setfield( L, -2, "is_rdonly" ); + + lua_pushboolean( L, vfs_item_is_hidden( stat ) ); + lua_setfield( L, -2, "is_hidden" ); + + lua_pushboolean( L, vfs_item_is_sys( stat ) ); + lua_setfield( L, -2, "is_sys" ); + + lua_pushboolean( L, vfs_item_is_arch( stat ) ); + lua_setfield( L, -2, "is_arch" ); + + // time stamp as sub-table + vfs_time tm; + int got_time = VFS_RES_OK == vfs_item_time( stat, &tm ) ? TRUE : FALSE; + + lua_createtable( L, 0, 6 ); + + lua_pushinteger( L, got_time ? tm.year : FILE_TIMEDEF_YEAR ); + lua_setfield( L, -2, "year" ); + + lua_pushinteger( L, got_time ? tm.mon : FILE_TIMEDEF_MON ); + lua_setfield( L, -2, "mon" ); + + lua_pushinteger( L, got_time ? tm.day : FILE_TIMEDEF_DAY ); + lua_setfield( L, -2, "day" ); + + lua_pushinteger( L, got_time ? tm.hour : FILE_TIMEDEF_HOUR ); + lua_setfield( L, -2, "hour" ); + + lua_pushinteger( L, got_time ? tm.min : FILE_TIMEDEF_MIN ); + lua_setfield( L, -2, "min" ); + + lua_pushinteger( L, got_time ? tm.sec : FILE_TIMEDEF_SEC ); + lua_setfield( L, -2, "sec" ); + + lua_setfield( L, -2, "time" ); + + return 1; +} + // g_read() static int file_g_read( lua_State* L, int n, int16_t end_char, int fd ) { @@ -561,6 +635,7 @@ static const LUA_REG_TYPE file_map[] = { { LSTRKEY( "exists" ), LFUNCVAL( file_exists ) }, { LSTRKEY( "fsinfo" ), LFUNCVAL( file_fsinfo ) }, { LSTRKEY( "on" ), LFUNCVAL( file_on ) }, + { LSTRKEY( "stat" ), LFUNCVAL( file_stat ) }, #ifdef BUILD_FATFS { LSTRKEY( "mount" ), LFUNCVAL( file_mount ) }, { LSTRKEY( "chdir" ), LFUNCVAL( file_chdir ) }, diff --git a/docs/en/modules/file.md b/docs/en/modules/file.md index 8fcee748..2b1b5b93 100644 --- a/docs/en/modules/file.md +++ b/docs/en/modules/file.md @@ -294,6 +294,55 @@ Renames a file. If a file is currently open, it will be closed first. file.rename("temp.lua","init.lua") ``` +## file.stat() + +Get attribtues of a file or directory in a table: + +- `size` file size in bytes +- `name` file name +- `time` table with time stamp information. Default is 1970-01-01 00:00:00 in case time stamps are not supported (on SPIFFS). + - `year` + - `mon` + - `day` + - `hour` + - `min` + - `sec` +- `is_dir` flag `true` if item is a directory, otherwise `false` +- `is_rdonly` flag `true` if item is read-only, otherwise `false` +- `is_hidden` flag `true` if item is hidden, otherwise `false` +- `is_sys` flag `true` if item is system, otherwise `false` +- `is_arch` flag `true` if item is archive, otherwise `false` + +#### Syntax +`file.stat(filename)` + +#### Parameters +`filename` file name + +#### Returns +table containing file attributes + +#### Example + +```lua +s = file.stat("/SD0/myfile") +print("name: " .. s.name) +print("size: " .. s.size) + +t = s.time +print(string.format("%02d:%02d:%02d", t.hour, t.min, t.sec)) +print(string.format("%04d-%02d-%02d", t.year, t.mon, t.day)) + +if s.is_dir then print("is directory") else print("is file") end +if s.is_rdonly then print("is read-only") else print("is writable") end +if s.is_hidden then print("is hidden") else print("is not hidden") end +if s.is_sys then print("is system") else print("is not system") end +if s.is_arch then print("is archive") else print("is not archive") end + +s = nil +t = nil +``` + # 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: