Ver código fonte

Add file.stat() to query file/directory attributes. (#1724)

* add file.stat()

* set default time/date to 1970-01-01
use lua_setfield
Arnim Läuger 7 anos atrás
pai
commit
305d7c4672
2 arquivos alterados com 130 adições e 6 exclusões
  1. 81 6
      app/modules/file.c
  2. 49 0
      docs/en/modules/file.md

+ 81 - 6
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 ) },

+ 49 - 0
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: