file.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. // Module for interfacing with file system
  2. #include "module.h"
  3. #include "lauxlib.h"
  4. #include "lmem.h"
  5. #include "platform.h"
  6. #include "spiffs/nodemcu_spiffs.h"
  7. #include <stdint.h>
  8. #include "vfs.h"
  9. #include <string.h>
  10. #include <alloca.h>
  11. #define FILE_READ_CHUNK 1024
  12. // use this time/date in absence of a timestamp
  13. #define FILE_TIMEDEF_YEAR 1970
  14. #define FILE_TIMEDEF_MON 01
  15. #define FILE_TIMEDEF_DAY 01
  16. #define FILE_TIMEDEF_HOUR 00
  17. #define FILE_TIMEDEF_MIN 00
  18. #define FILE_TIMEDEF_SEC 00
  19. static int file_fd = 0;
  20. static int file_fd_ref = LUA_NOREF;
  21. static int rtc_cb_ref = LUA_NOREF;
  22. typedef struct _file_fd_ud {
  23. int fd;
  24. } file_fd_ud;
  25. static void do_flash_mount() {
  26. if (!vfs_mount("/FLASH", 0)) {
  27. // Failed to mount -- try reformat
  28. dbg_printf("Formatting file system. Please wait...\n");
  29. if (!vfs_format()) {
  30. NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
  31. NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
  32. }
  33. }
  34. }
  35. static void table2tm( lua_State *L, vfs_time *tm )
  36. {
  37. int idx = lua_gettop( L );
  38. // extract items from table
  39. lua_getfield( L, idx, "year" );
  40. lua_getfield( L, idx, "mon" );
  41. lua_getfield( L, idx, "day" );
  42. lua_getfield( L, idx, "hour" );
  43. lua_getfield( L, idx, "min" );
  44. lua_getfield( L, idx, "sec" );
  45. tm->year = luaL_optint( L, ++idx, FILE_TIMEDEF_YEAR );
  46. tm->mon = luaL_optint( L, ++idx, FILE_TIMEDEF_MON );
  47. tm->day = luaL_optint( L, ++idx, FILE_TIMEDEF_DAY );
  48. tm->hour = luaL_optint( L, ++idx, FILE_TIMEDEF_HOUR );
  49. tm->min = luaL_optint( L, ++idx, FILE_TIMEDEF_MIN );
  50. tm->sec = luaL_optint( L, ++idx, FILE_TIMEDEF_SEC );
  51. // remove items from stack
  52. lua_pop( L, 6 );
  53. }
  54. static sint32_t file_rtc_cb( vfs_time *tm )
  55. {
  56. sint32_t res = VFS_RES_ERR;
  57. if (rtc_cb_ref != LUA_NOREF) {
  58. lua_State *L = lua_getstate();
  59. lua_rawgeti( L, LUA_REGISTRYINDEX, rtc_cb_ref );
  60. if (luaL_pcallx( L, 0, 1 ) != LUA_OK)
  61. return res;
  62. if (lua_type( L, lua_gettop( L ) ) == LUA_TTABLE) {
  63. table2tm( L, tm );
  64. res = VFS_RES_OK;
  65. }
  66. // pop item returned by callback
  67. lua_pop( L, 1 );
  68. }
  69. return res;
  70. }
  71. // Lua: on()
  72. static int file_on(lua_State *L)
  73. {
  74. enum events{
  75. ON_RTC = 0
  76. };
  77. const char *const eventnames[] = {"rtc", NULL};
  78. int event = luaL_checkoption(L, 1, "rtc", eventnames);
  79. switch (event) {
  80. case ON_RTC:
  81. luaL_unref(L, LUA_REGISTRYINDEX, rtc_cb_ref);
  82. switch(lua_type(L, 2)) {
  83. case LUA_TFUNCTION:
  84. lua_pushvalue(L, 2); // copy argument (func) to the top of stack
  85. rtc_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  86. vfs_register_rtc_cb(file_rtc_cb);
  87. break;
  88. case LUA_TNIL:
  89. rtc_cb_ref = LUA_NOREF;
  90. vfs_register_rtc_cb(NULL);
  91. break;
  92. default:
  93. luaL_error(L, "Callback should be function or nil");
  94. }
  95. break;
  96. default:
  97. break;
  98. }
  99. return 0;
  100. }
  101. // Lua: close()
  102. static int file_close( lua_State* L )
  103. {
  104. int need_pop = FALSE;
  105. file_fd_ud *ud;
  106. if (lua_type( L, 1 ) != LUA_TUSERDATA) {
  107. // fall back to last opened file
  108. if (file_fd_ref != LUA_NOREF) {
  109. lua_rawgeti( L, LUA_REGISTRYINDEX, file_fd_ref );
  110. // top of stack is now default file descriptor
  111. ud = (file_fd_ud *)luaL_checkudata(L, -1, "file.obj");
  112. lua_pop( L, 1 );
  113. } else {
  114. // no default file currently opened
  115. return 0;
  116. }
  117. } else {
  118. ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
  119. }
  120. if(ud->fd){
  121. vfs_close(ud->fd);
  122. // mark as closed
  123. ud->fd = 0;
  124. }
  125. // unref default file descriptor
  126. luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
  127. file_fd_ref = LUA_NOREF;
  128. return 0;
  129. }
  130. static int file_obj_free( lua_State *L )
  131. {
  132. file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
  133. if (ud->fd) {
  134. // close file if it's still open
  135. vfs_close(ud->fd);
  136. ud->fd = 0;
  137. }
  138. return 0;
  139. }
  140. // Lua: format()
  141. static int file_format( lua_State* L )
  142. {
  143. size_t len;
  144. file_close(L);
  145. if( !vfs_format() )
  146. {
  147. NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
  148. NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
  149. luaL_error(L, "Failed to format file system");
  150. }
  151. else{
  152. NODE_ERR( "format done.\n" );
  153. }
  154. return 0;
  155. }
  156. static int file_fscfg (lua_State *L)
  157. {
  158. uint32_t phys_addr, phys_size;
  159. vfs_fscfg("/FLASH", &phys_addr, &phys_size);
  160. lua_pushinteger (L, phys_addr);
  161. lua_pushinteger (L, phys_size);
  162. return 2;
  163. }
  164. // Lua: open(filename, mode)
  165. static int file_open( lua_State* L )
  166. {
  167. size_t len;
  168. // unref last file descriptor to allow gc'ing if not kept by user script
  169. luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
  170. file_fd_ref = LUA_NOREF;
  171. const char *fname = luaL_checklstring( L, 1, &len );
  172. const char *basename = vfs_basename( fname );
  173. luaL_argcheck(L, strlen(basename) <= FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid");
  174. const char *mode = luaL_optstring(L, 2, "r");
  175. file_fd = vfs_open(fname, mode);
  176. if(!file_fd){
  177. lua_pushnil(L);
  178. } else {
  179. file_fd_ud *ud = (file_fd_ud *) lua_newuserdata( L, sizeof( file_fd_ud ) );
  180. ud->fd = file_fd;
  181. luaL_getmetatable( L, "file.obj" );
  182. lua_setmetatable( L, -2 );
  183. // store reference to opened file
  184. lua_pushvalue( L, -1 );
  185. file_fd_ref = luaL_ref( L, LUA_REGISTRYINDEX );
  186. }
  187. return 1;
  188. }
  189. // Lua: list()
  190. static int file_list( lua_State* L )
  191. {
  192. vfs_dir *dir;
  193. const char *pattern;
  194. struct vfs_stat stat;
  195. int pcres;
  196. lua_settop(L, 1);
  197. pattern = luaL_optstring(L, 1, NULL); /* Pattern (arg) or nil (not) at 1 */
  198. dir = vfs_opendir("");
  199. if (dir == NULL) {
  200. return 0;
  201. }
  202. lua_newtable( L ); /* Table at 2 */
  203. if (pattern) {
  204. /*
  205. * We know that pattern is a string, and so the "match" method will always
  206. * exist. No need to check return value here
  207. */
  208. luaL_getmetafield( L, 1, "match" ); /* Function at 3 */
  209. }
  210. while (vfs_readdir(dir, &stat) == VFS_RES_OK) {
  211. if (pattern) {
  212. lua_settop( L, 3 ); /* Ensure nothing else on stack */
  213. /* Construct and pcall(string.match,name,pattern) */
  214. lua_pushvalue( L, 3 );
  215. lua_pushstring( L, stat.name );
  216. lua_pushvalue( L, 1 );
  217. pcres = lua_pcall( L, 2, 1, 0 );
  218. if (pcres != 0) {
  219. vfs_closedir(dir);
  220. lua_error( L );
  221. }
  222. if (lua_isnil( L, -1 )) {
  223. continue;
  224. }
  225. }
  226. lua_pushinteger( L, stat.size );
  227. lua_setfield( L, 2, stat.name );
  228. }
  229. /* Shed everything back to Table */
  230. lua_settop( L, 2 );
  231. vfs_closedir(dir);
  232. return 1;
  233. }
  234. static int get_file_obj( lua_State *L, int *argpos )
  235. {
  236. if (lua_type( L, 1 ) == LUA_TUSERDATA) {
  237. file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
  238. *argpos = 2;
  239. return ud->fd;
  240. } else {
  241. *argpos = 1;
  242. return file_fd;
  243. }
  244. }
  245. #define GET_FILE_OBJ int argpos; \
  246. int fd = get_file_obj( L, &argpos );
  247. static int file_seek (lua_State *L)
  248. {
  249. GET_FILE_OBJ;
  250. static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END};
  251. static const char *const modenames[] = {"set", "cur", "end", NULL};
  252. if(!fd)
  253. return luaL_error(L, "open a file first");
  254. int op = luaL_checkoption(L, argpos, "cur", modenames);
  255. long offset = luaL_optlong(L, ++argpos, 0);
  256. op = vfs_lseek(fd, offset, mode[op]);
  257. if (op < 0)
  258. lua_pushnil(L); /* error */
  259. else
  260. lua_pushinteger(L, vfs_tell(fd));
  261. return 1;
  262. }
  263. // Lua: exists(filename)
  264. static int file_exists( lua_State* L )
  265. {
  266. size_t len;
  267. const char *fname = luaL_checklstring( L, 1, &len );
  268. const char *basename = vfs_basename( fname );
  269. luaL_argcheck(L, strlen(basename) <= FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid");
  270. struct vfs_stat stat;
  271. lua_pushboolean(L, vfs_stat((char *)fname, &stat) == VFS_RES_OK ? 1 : 0);
  272. return 1;
  273. }
  274. // Lua: remove(filename)
  275. static int file_remove( lua_State* L )
  276. {
  277. size_t len;
  278. const char *fname = luaL_checklstring( L, 1, &len );
  279. const char *basename = vfs_basename( fname );
  280. luaL_argcheck(L, strlen(basename) <= FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid");
  281. vfs_remove((char *)fname);
  282. return 0;
  283. }
  284. // Lua: flush()
  285. static int file_flush( lua_State* L )
  286. {
  287. GET_FILE_OBJ;
  288. if(!fd)
  289. return luaL_error(L, "open a file first");
  290. if(vfs_flush(fd) == 0)
  291. lua_pushboolean(L, 1);
  292. else
  293. lua_pushnil(L);
  294. return 1;
  295. }
  296. // Lua: rename("oldname", "newname")
  297. static int file_rename( lua_State* L )
  298. {
  299. size_t len;
  300. const char *oldname = luaL_checklstring( L, 1, &len );
  301. const char *basename = vfs_basename( oldname );
  302. luaL_argcheck(L, strlen(basename) <= FS_OBJ_NAME_LEN && strlen(oldname) == len, 1, "filename invalid");
  303. const char *newname = luaL_checklstring( L, 2, &len );
  304. basename = vfs_basename( newname );
  305. luaL_argcheck(L, strlen(basename) <= FS_OBJ_NAME_LEN && strlen(newname) == len, 2, "filename invalid");
  306. if(0 <= vfs_rename( oldname, newname )){
  307. lua_pushboolean(L, 1);
  308. } else {
  309. lua_pushboolean(L, 0);
  310. }
  311. return 1;
  312. }
  313. // Lua: stat(filename)
  314. static int file_stat( lua_State* L )
  315. {
  316. size_t len;
  317. const char *fname = luaL_checklstring( L, 1, &len );
  318. luaL_argcheck( L, strlen(fname) <= FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid" );
  319. struct vfs_stat stat;
  320. if (vfs_stat( (char *)fname, &stat ) != VFS_RES_OK) {
  321. lua_pushnil( L );
  322. return 1;
  323. }
  324. lua_createtable( L, 0, 7 );
  325. lua_pushinteger( L, stat.size );
  326. lua_setfield( L, -2, "size" );
  327. lua_pushstring( L, stat.name );
  328. lua_setfield( L, -2, "name" );
  329. lua_pushboolean( L, stat.is_dir );
  330. lua_setfield( L, -2, "is_dir" );
  331. lua_pushboolean( L, stat.is_rdonly );
  332. lua_setfield( L, -2, "is_rdonly" );
  333. lua_pushboolean( L, stat.is_hidden );
  334. lua_setfield( L, -2, "is_hidden" );
  335. lua_pushboolean( L, stat.is_sys );
  336. lua_setfield( L, -2, "is_sys" );
  337. lua_pushboolean( L, stat.is_arch );
  338. lua_setfield( L, -2, "is_arch" );
  339. // time stamp as sub-table
  340. lua_createtable( L, 0, 6 );
  341. lua_pushinteger( L, stat.tm_valid ? stat.tm.year : FILE_TIMEDEF_YEAR );
  342. lua_setfield( L, -2, "year" );
  343. lua_pushinteger( L, stat.tm_valid ? stat.tm.mon : FILE_TIMEDEF_MON );
  344. lua_setfield( L, -2, "mon" );
  345. lua_pushinteger( L, stat.tm_valid ? stat.tm.day : FILE_TIMEDEF_DAY );
  346. lua_setfield( L, -2, "day" );
  347. lua_pushinteger( L, stat.tm_valid ? stat.tm.hour : FILE_TIMEDEF_HOUR );
  348. lua_setfield( L, -2, "hour" );
  349. lua_pushinteger( L, stat.tm_valid ? stat.tm.min : FILE_TIMEDEF_MIN );
  350. lua_setfield( L, -2, "min" );
  351. lua_pushinteger( L, stat.tm_valid ? stat.tm.sec : FILE_TIMEDEF_SEC );
  352. lua_setfield( L, -2, "sec" );
  353. lua_setfield( L, -2, "time" );
  354. return 1;
  355. }
  356. // g_read()
  357. static int file_g_read( lua_State* L, int n, int16_t end_char, int fd )
  358. {
  359. int i, j;
  360. luaL_Buffer b;
  361. char p[LUAL_BUFFERSIZE/2];
  362. if(!fd)
  363. return luaL_error(L, "open a file first");
  364. luaL_buffinit(L, &b);
  365. for (j = 0; j < n; j += sizeof(p)) {
  366. int nwanted = (n - j >= sizeof(p)) ? sizeof(p) : n - j;
  367. int nread = vfs_read(fd, p, nwanted);
  368. if (nread == VFS_RES_ERR || nread == 0) {
  369. if (j > 0) {
  370. break;
  371. }
  372. lua_pushnil(L);
  373. return 1;
  374. }
  375. for (i = 0; i < nread; ++i) {
  376. luaL_addchar(&b, p[i]);
  377. if (p[i] == end_char) {
  378. vfs_lseek(fd, -nread + i + 1, VFS_SEEK_CUR); //reposition after end char found
  379. nread = 0; // force break on outer loop
  380. break;
  381. }
  382. }
  383. if (nread < nwanted)
  384. break;
  385. }
  386. luaL_pushresult(&b);
  387. return 1;
  388. }
  389. // Lua: read()
  390. // file.read() will read FILE _CHUNK bytes, or EOF is reached.
  391. // file.read(10) will read 10 byte from file, or EOF is reached.
  392. // file.read('q') will read until 'q' or EOF is reached.
  393. static int file_read( lua_State* L )
  394. {
  395. unsigned need_len = FILE_READ_CHUNK;
  396. int16_t end_char = EOF;
  397. size_t el;
  398. GET_FILE_OBJ;
  399. if( lua_type( L, argpos ) == LUA_TNUMBER )
  400. {
  401. need_len = ( unsigned )luaL_checkinteger( L, argpos );
  402. }
  403. else if(lua_isstring(L, argpos))
  404. {
  405. const char *end = luaL_checklstring( L, argpos, &el );
  406. if(el!=1){
  407. return luaL_error( L, "wrong arg range" );
  408. }
  409. end_char = (int16_t)end[0];
  410. }
  411. return file_g_read(L, need_len, end_char, fd);
  412. }
  413. // Lua: readline()
  414. static int file_readline( lua_State* L )
  415. {
  416. GET_FILE_OBJ;
  417. return file_g_read(L, FILE_READ_CHUNK, '\n', fd);
  418. }
  419. // Lua: getfile(filename)
  420. static int file_getfile( lua_State* L )
  421. {
  422. // Warning this code C calls other file_* routines to avoid duplication code. These
  423. // use Lua stack addressing of arguments, so this does Lua stack maniplation to
  424. // align these
  425. int ret_cnt = 0;
  426. lua_settop(L ,1);
  427. // Stack [1] = FD
  428. file_open(L);
  429. // Stack [1] = filename; [2] = FD or nil
  430. if (!lua_isnil(L, -1)) {
  431. lua_remove(L, 1); // dump filename, so [1] = FD
  432. file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
  433. ret_cnt = file_g_read(L, LUAI_MAXINT32, EOF, ud->fd);
  434. // Stack [1] = FD; [2] = contents if ret_cnt = 1;
  435. file_close(L); // leaves Stack unchanged if [1] = FD
  436. lua_remove(L, 1); // Dump FD leaving contents as [1] / ToS
  437. }
  438. return ret_cnt;
  439. }
  440. // Lua: write("string")
  441. static int file_write( lua_State* L )
  442. {
  443. GET_FILE_OBJ;
  444. if(!fd)
  445. return luaL_error(L, "open a file first");
  446. size_t l, rl;
  447. const char *s = luaL_checklstring(L, argpos, &l);
  448. rl = vfs_write(fd, s, l);
  449. if(rl==l)
  450. lua_pushboolean(L, 1);
  451. else
  452. lua_pushnil(L);
  453. return 1;
  454. }
  455. // Lua: writeline("string")
  456. static int file_writeline( lua_State* L )
  457. {
  458. GET_FILE_OBJ;
  459. if(!fd)
  460. return luaL_error(L, "open a file first");
  461. size_t l, rl;
  462. const char *s = luaL_checklstring(L, argpos, &l);
  463. rl = vfs_write(fd, s, l);
  464. if(rl==l){
  465. rl = vfs_write(fd, "\n", 1);
  466. if(rl==1)
  467. lua_pushboolean(L, 1);
  468. else
  469. lua_pushnil(L);
  470. }
  471. else{
  472. lua_pushnil(L);
  473. }
  474. return 1;
  475. }
  476. // Lua: getfile(filename)
  477. static int file_putfile( lua_State* L )
  478. {
  479. // Warning this code C calls other file_* routines to avoid duplication code. These
  480. // use Lua stack addressing of arguments, so this does Lua stack maniplation to
  481. // align these
  482. int ret_cnt = 0;
  483. lua_settop(L, 2);
  484. lua_pushvalue(L, 2); //dup contents onto the ToS [3]
  485. lua_pushliteral(L, "w+");
  486. lua_replace(L, 2);
  487. // Stack [1] = filename; [2] "w+" [3] contents;
  488. file_open(L);
  489. // Stack [1] = filename; [2] "w+" [3] contents; [4] FD or nil
  490. if (!lua_isnil(L, -1)) {
  491. lua_remove(L, 2); //dump "w+" attribute literal
  492. lua_replace(L, 1);
  493. // Stack [1] = FD; [2] contents
  494. file_write(L);
  495. // Stack [1] = FD; [2] contents; [3] result status
  496. lua_remove(L, 2); //dump contents
  497. file_close(L);
  498. lua_remove(L, 1); // Dump FD leaving status as ToS
  499. }
  500. return 1;
  501. }
  502. // Lua: fsinfo()
  503. static int file_fsinfo( lua_State* L )
  504. {
  505. uint32_t total, used;
  506. if (vfs_fsinfo("", &total, &used)) {
  507. return luaL_error(L, "file system failed");
  508. }
  509. NODE_DBG("total: %d, used:%d\n", total, used);
  510. if(total>0x7FFFFFFF || used>0x7FFFFFFF || used > total)
  511. {
  512. return luaL_error(L, "file system error");
  513. }
  514. lua_pushinteger(L, total-used);
  515. lua_pushinteger(L, used);
  516. lua_pushinteger(L, total);
  517. return 3;
  518. }
  519. typedef struct {
  520. vfs_vol *vol;
  521. } volume_type;
  522. // Lua: vol = file.mount("/SD0")
  523. static int file_mount( lua_State *L )
  524. {
  525. const char *ldrv = luaL_checkstring( L, 1 );
  526. int num = luaL_optint( L, 2, -1 );
  527. volume_type *vol = (volume_type *)lua_newuserdata( L, sizeof( volume_type ) );
  528. if (vol->vol = vfs_mount( ldrv, num )) {
  529. /* set its metatable */
  530. luaL_getmetatable(L, "file.vol");
  531. lua_setmetatable(L, -2);
  532. return 1;
  533. } else {
  534. // remove created userdata
  535. lua_pop( L, 1 );
  536. return 0;
  537. }
  538. }
  539. // Lua: success = file.chdir("/SD0/")
  540. static int file_chdir( lua_State *L )
  541. {
  542. const char *path = luaL_checkstring( L, 1 );
  543. lua_pushboolean( L, 0 <= vfs_chdir( path ) );
  544. return 1;
  545. }
  546. static int file_vol_umount( lua_State *L )
  547. {
  548. volume_type *vol = luaL_checkudata( L, 1, "file.vol" );
  549. luaL_argcheck( L, vol, 1, "volume expected" );
  550. lua_pushboolean( L, 0 <= vfs_umount( vol->vol ) );
  551. // invalidate vfs descriptor, it has been free'd anyway
  552. vol->vol = NULL;
  553. return 1;
  554. }
  555. LROT_BEGIN(file_obj, NULL, LROT_MASK_GC_INDEX)
  556. LROT_FUNCENTRY( __gc, file_obj_free )
  557. LROT_TABENTRY( __index, file_obj )
  558. LROT_FUNCENTRY( close, file_close )
  559. LROT_FUNCENTRY( read, file_read )
  560. LROT_FUNCENTRY( readline, file_readline )
  561. LROT_FUNCENTRY( write, file_write )
  562. LROT_FUNCENTRY( writeline, file_writeline )
  563. LROT_FUNCENTRY( seek, file_seek )
  564. LROT_FUNCENTRY( flush, file_flush )
  565. LROT_END(file_obj, NULL, LROT_MASK_GC_INDEX)
  566. LROT_BEGIN(file_vol, NULL, LROT_MASK_INDEX)
  567. LROT_TABENTRY( __index, file_vol )
  568. LROT_FUNCENTRY( umount, file_vol_umount )
  569. LROT_END(file_vol, NULL, LROT_MASK_INDEX)
  570. // Module function map
  571. LROT_BEGIN(file, NULL, 0)
  572. LROT_FUNCENTRY( list, file_list )
  573. LROT_FUNCENTRY( open, file_open )
  574. LROT_FUNCENTRY( close, file_close )
  575. LROT_FUNCENTRY( write, file_write )
  576. LROT_FUNCENTRY( writeline, file_writeline )
  577. LROT_FUNCENTRY( read, file_read )
  578. LROT_FUNCENTRY( readline, file_readline )
  579. #ifdef BUILD_SPIFFS
  580. LROT_FUNCENTRY( format, file_format )
  581. LROT_FUNCENTRY( fscfg, file_fscfg )
  582. #endif
  583. LROT_FUNCENTRY( remove, file_remove )
  584. LROT_FUNCENTRY( seek, file_seek )
  585. LROT_FUNCENTRY( flush, file_flush )
  586. LROT_FUNCENTRY( rename, file_rename )
  587. LROT_FUNCENTRY( exists, file_exists )
  588. LROT_FUNCENTRY( getcontents, file_getfile )
  589. LROT_FUNCENTRY( putcontents, file_putfile )
  590. LROT_FUNCENTRY( fsinfo, file_fsinfo )
  591. LROT_FUNCENTRY( on, file_on )
  592. LROT_FUNCENTRY( stat, file_stat )
  593. #ifdef BUILD_FATFS
  594. LROT_FUNCENTRY( mount, file_mount )
  595. LROT_FUNCENTRY( chdir, file_chdir )
  596. #endif
  597. LROT_END(file, NULL, 0)
  598. int luaopen_file( lua_State *L ) {
  599. int startup_option = platform_rcr_get_startup_option();
  600. if ((startup_option & STARTUP_OPTION_DELAY_MOUNT) == 0) {
  601. do_flash_mount();
  602. } else {
  603. myspiffs_set_automount(do_flash_mount);
  604. }
  605. luaL_rometatable( L, "file.vol", LROT_TABLEREF(file_vol));
  606. luaL_rometatable( L, "file.obj", LROT_TABLEREF(file_obj));
  607. return 0;
  608. }
  609. NODEMCU_MODULE(FILE, "file", file, luaopen_file);