liolib.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296
  1. /*
  2. @module io
  3. @summary io操作(扩展)
  4. @version 1.0
  5. @date 2020.07.03
  6. @tag LUAT_USE_GPIO
  7. @demo fs
  8. @usage
  9. -- io模块是lua原生模块,LuatOS增加了一些API
  10. -- 请配合os模块一起使用
  11. -- 只读模式, 打开文件
  12. local fd = io.open("/xxx.txt", "rb")
  13. -- 读写默认,打开文件
  14. local fd = io.open("/xxx.txt", "wb")
  15. -- 写入文件,且截断为0字节
  16. local fd = io.open("/xxx.txt", "wb+")
  17. -- 追加模式
  18. local fd = io.open("/xxx.txt", "a")
  19. -- 若文件打开成功, fd不为nil,否则就是失败了
  20. -- 注意, 刷机时所添加的文件, 均在 /luadb 目录下, 只读
  21. if fd then
  22. -- 读取指定字节数,如果数据不足,就只返回实际长度的数据
  23. local data = fd:read(12)
  24. -- 按行读取
  25. local line = fd:read("*l")
  26. -- 全部读取
  27. local line = fd:read("*a")
  28. -- 数据写入, 仅w或a模式可调用
  29. -- 数据需要是字符串, lua的字符串是带长度的,可以包含任何二进制数据
  30. fd:write("xxxx")
  31. -- 以下是写入0x12, 0x13
  32. fd:write(string.char(0x12, 0x13))
  33. -- 移动句柄,绝对坐标
  34. fd:seek(1024, io.SEEK_SET)
  35. -- 移动句柄,相对坐标
  36. fd:seek(1024, io.SEEK_CUR)
  37. -- 移动句柄,反向绝对坐标,从文件结尾往文件头部算
  38. fd:seek(124, io.SEEK_END)
  39. -- 执行完操作后,一定要关掉文件
  40. fd:close()
  41. end
  42. */
  43. #define liolib_c
  44. #define LUA_LIB
  45. #include "lprefix.h"
  46. #include <ctype.h>
  47. #include <errno.h>
  48. #include <locale.h>
  49. #include <stdio.h>
  50. #include <stdlib.h>
  51. #include <string.h>
  52. #include "lua.h"
  53. #include "lauxlib.h"
  54. #include "lualib.h"
  55. #define l_fseek luat_fs_fseek
  56. #define l_ftell luat_fs_ftell
  57. #define l_seeknum int32_t
  58. /*
  59. ** Change this macro to accept other modes for 'fopen' besides
  60. ** the standard ones.
  61. */
  62. #if !defined(l_checkmode)
  63. /* accepted extensions to 'mode' in 'fopen' */
  64. #if !defined(L_MODEEXT)
  65. #define L_MODEEXT "b"
  66. #endif
  67. /* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */
  68. static int l_checkmode (const char *mode) {
  69. return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL &&
  70. (*mode != '+' || (++mode, 1)) && /* skip if char is '+' */
  71. (strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */
  72. }
  73. #endif
  74. /*
  75. ** {======================================================
  76. ** l_popen spawns a new process connected to the current
  77. ** one through the file streams.
  78. ** =======================================================
  79. */
  80. #if !defined(l_popen) /* { */
  81. #if defined(LUA_USE_POSIX) /* { */
  82. #define l_popen(L,c,m) (fflush(NULL), popen(c,m))
  83. #define l_pclose(L,file) (pclose(file))
  84. #elif defined(LUA_USE_WINDOWS) /* }{ */
  85. #define l_popen(L,c,m) (_popen(c,m))
  86. #define l_pclose(L,file) (_pclose(file))
  87. #else /* }{ */
  88. /* ISO C definitions */
  89. #define l_popen(L,c,m) \
  90. ((void)((void)c, m), \
  91. luaL_error(L, "'popen' not supported"), \
  92. (FILE*)0)
  93. #define l_pclose(L,file) ((void)L, (void)file, -1)
  94. #endif /* } */
  95. #endif /* } */
  96. /* }====================================================== */
  97. #if !defined(l_getc) /* { */
  98. // #if defined(LUA_USE_POSIX)
  99. // #define l_getc(f) getc_unlocked(f)
  100. // #define l_lockfile(f) flockfile(f)
  101. // #define l_unlockfile(f) funlockfile(f)
  102. // #else
  103. int luat_fs_getc(FILE* stream);
  104. #define l_getc(f) luat_fs_getc(f)
  105. #define l_lockfile(f) ((void)0)
  106. #define l_unlockfile(f) ((void)0)
  107. // #endif
  108. #endif /* } */
  109. /*
  110. ** {======================================================
  111. ** l_fseek: configuration for longer offsets
  112. ** =======================================================
  113. */
  114. #if !defined(l_fseek) /* { */
  115. #if defined(LUA_USE_POSIX) /* { */
  116. #include <sys/types.h>
  117. #define l_fseek(f,o,w) fseeko(f,o,w)
  118. #define l_ftell(f) ftello(f)
  119. #define l_seeknum off_t
  120. #elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \
  121. && defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */
  122. /* Windows (but not DDK) and Visual C++ 2005 or higher */
  123. #define l_fseek(f,o,w) _fseeki64(f,o,w)
  124. #define l_ftell(f) _ftelli64(f)
  125. #define l_seeknum __int64
  126. #else /* }{ */
  127. /* ISO C definitions */
  128. #define l_fseek(f,o,w) fseek(f,o,w)
  129. #define l_ftell(f) ftell(f)
  130. #define l_seeknum long
  131. #endif /* } */
  132. #endif /* } */
  133. /* }====================================================== */
  134. #define IO_PREFIX "_IO_"
  135. #define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1)
  136. #define IO_INPUT (IO_PREFIX "input")
  137. #define IO_OUTPUT (IO_PREFIX "output")
  138. typedef luaL_Stream LStream;
  139. #define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE))
  140. #define isclosed(p) ((p)->closef == NULL)
  141. #include "luat_fs.h"
  142. #define LUAT_LOG_TAG "io"
  143. #include "luat_log.h"
  144. #undef fopen
  145. #undef fclose
  146. #undef fread
  147. #undef fseek
  148. #undef feof
  149. #undef ferror
  150. #undef fwrite
  151. #define fopen luat_fs_fopen
  152. #define fclose luat_fs_fclose
  153. #define fread luat_fs_fread
  154. #define fseek luat_fs_fseek
  155. #define ferror luat_fs_ferror
  156. #define feof luat_fs_feof
  157. #define fwrite luat_fs_fwrite
  158. #define ftell luat_fs_ftell
  159. static int io_type (lua_State *L) {
  160. LStream *p;
  161. luaL_checkany(L, 1);
  162. p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE);
  163. if (p == NULL)
  164. lua_pushnil(L); /* not a file */
  165. else if (isclosed(p))
  166. lua_pushliteral(L, "closed file");
  167. else
  168. lua_pushliteral(L, "file");
  169. return 1;
  170. }
  171. static int f_tostring (lua_State *L) {
  172. LStream *p = tolstream(L);
  173. if (isclosed(p))
  174. lua_pushliteral(L, "file (closed)");
  175. else
  176. lua_pushfstring(L, "file (%p)", p->f);
  177. return 1;
  178. }
  179. static FILE *tofile (lua_State *L) {
  180. LStream *p = tolstream(L);
  181. if (isclosed(p))
  182. luaL_error(L, "attempt to use a closed file");
  183. lua_assert(p->f);
  184. return p->f;
  185. }
  186. /*
  187. ** When creating file handles, always creates a 'closed' file handle
  188. ** before opening the actual file; so, if there is a memory error, the
  189. ** handle is in a consistent state.
  190. */
  191. static LStream *newprefile (lua_State *L) {
  192. LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream));
  193. p->closef = NULL; /* mark file handle as 'closed' */
  194. luaL_setmetatable(L, LUA_FILEHANDLE);
  195. return p;
  196. }
  197. /*
  198. ** Calls the 'close' function from a file handle. The 'volatile' avoids
  199. ** a bug in some versions of the Clang compiler (e.g., clang 3.0 for
  200. ** 32 bits).
  201. */
  202. static int aux_close (lua_State *L) {
  203. LStream *p = tolstream(L);
  204. volatile lua_CFunction cf = p->closef;
  205. p->closef = NULL; /* mark stream as closed */
  206. return (*cf)(L); /* close it */
  207. }
  208. static int f_close (lua_State *L) {
  209. tofile(L); /* make sure argument is an open stream */
  210. return aux_close(L);
  211. }
  212. #ifdef LUA_USE_WINDOWS
  213. static int io_close (lua_State *L) {
  214. if (lua_isnone(L, 1)) /* no argument? */
  215. lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */
  216. return f_close(L);
  217. }
  218. #endif
  219. static int f_gc (lua_State *L) {
  220. LStream *p = tolstream(L);
  221. if (!isclosed(p) && p->f != NULL)
  222. aux_close(L); /* ignore closed and incompletely open files */
  223. return 0;
  224. }
  225. /*
  226. ** function to close regular files
  227. */
  228. static int io_fclose (lua_State *L) {
  229. LStream *p = tolstream(L);
  230. int res = fclose(p->f);
  231. return luaL_fileresult(L, (res == 0), NULL);
  232. }
  233. static LStream *newfile (lua_State *L) {
  234. LStream *p = newprefile(L);
  235. p->f = NULL;
  236. p->closef = &io_fclose;
  237. return p;
  238. }
  239. static void opencheck (lua_State *L, const char *fname, const char *mode) {
  240. LStream *p = newfile(L);
  241. p->f = fopen(fname, mode);
  242. if (p->f == NULL)
  243. luaL_error(L, "cannot open file '%s' (%d)", fname, errno);
  244. }
  245. static int io_open (lua_State *L) {
  246. const char *filename = luaL_checkstring(L, 1);
  247. const char *mode = luaL_optstring(L, 2, "r");
  248. LStream *p = newfile(L);
  249. const char *md = mode; /* to traverse/check mode */
  250. luaL_argcheck(L, l_checkmode(md), 2, "invalid mode");
  251. p->f = fopen(filename, mode);
  252. return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
  253. }
  254. #ifdef LUA_USE_WINDOWS
  255. /*
  256. ** function to close 'popen' files
  257. */
  258. static int io_pclose (lua_State *L) {
  259. LStream *p = tolstream(L);
  260. int ret = luaL_execresult(L, l_pclose(L, p->f));
  261. #ifdef LUAT_USE_FS_VFS
  262. luat_vfs_rm_fd(p->f);
  263. #endif
  264. return ret;
  265. }
  266. static int io_popen (lua_State *L) {
  267. const char *filename = luaL_checkstring(L, 1);
  268. const char *mode = luaL_optstring(L, 2, "r");
  269. LStream *p = newprefile(L);
  270. p->f = l_popen(L, filename, mode);
  271. p->closef = &io_pclose;
  272. #ifdef LUAT_USE_FS_VFS
  273. if (p->f) {
  274. FILE* tmp = luat_vfs_add_fd(p->f, NULL);
  275. if (tmp == NULL) {
  276. l_pclose(L, p->f);
  277. p->f = NULL;
  278. p->closef = NULL;
  279. }
  280. else {
  281. //printf("replace p->f %p => %p\n", p->f, tmp);
  282. p->f = tmp;
  283. }
  284. }
  285. #endif
  286. return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
  287. }
  288. static int io_tmpfile (lua_State *L) {
  289. LStream *p = newfile(L);
  290. p->f = tmpfile();
  291. return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
  292. }
  293. static FILE *getiofile (lua_State *L, const char *findex) {
  294. LStream *p;
  295. lua_getfield(L, LUA_REGISTRYINDEX, findex);
  296. p = (LStream *)lua_touserdata(L, -1);
  297. if (isclosed(p))
  298. luaL_error(L, "standard %s file is closed", findex + IOPREF_LEN);
  299. return p->f;
  300. }
  301. static int g_iofile (lua_State *L, const char *f, const char *mode) {
  302. if (!lua_isnoneornil(L, 1)) {
  303. const char *filename = lua_tostring(L, 1);
  304. if (filename)
  305. opencheck(L, filename, mode);
  306. else {
  307. tofile(L); /* check that it's a valid file handle */
  308. lua_pushvalue(L, 1);
  309. }
  310. lua_setfield(L, LUA_REGISTRYINDEX, f);
  311. }
  312. /* return current value */
  313. lua_getfield(L, LUA_REGISTRYINDEX, f);
  314. return 1;
  315. }
  316. static int io_input (lua_State *L) {
  317. return g_iofile(L, IO_INPUT, "r");
  318. }
  319. static int io_output (lua_State *L) {
  320. return g_iofile(L, IO_OUTPUT, "w");
  321. }
  322. #endif
  323. static int io_readline (lua_State *L);
  324. /*
  325. ** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit
  326. ** in the limit for upvalues of a closure)
  327. */
  328. #define MAXARGLINE 250
  329. static void aux_lines (lua_State *L, int toclose) {
  330. int n = lua_gettop(L) - 1; /* number of arguments to read */
  331. luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments");
  332. lua_pushinteger(L, n); /* number of arguments to read */
  333. lua_pushboolean(L, toclose); /* close/not close file when finished */
  334. lua_rotate(L, 2, 2); /* move 'n' and 'toclose' to their positions */
  335. lua_pushcclosure(L, io_readline, 3 + n);
  336. }
  337. static int f_lines (lua_State *L) {
  338. tofile(L); /* check that it's a valid file handle */
  339. aux_lines(L, 0);
  340. return 1;
  341. }
  342. // static void opencheck (lua_State *L, const char *fname, const char *mode) {
  343. // LStream *p = newfile(L);
  344. // p->f = fopen(fname, mode);
  345. // if (p->f == NULL)
  346. // luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno));
  347. // }
  348. static int io_lines (lua_State *L) {
  349. int toclose;
  350. if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */
  351. if (lua_isnil(L, 1)) { /* no file name? */
  352. lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */
  353. lua_replace(L, 1); /* put it at index 1 */
  354. tofile(L); /* check that it's a valid file handle */
  355. toclose = 0; /* do not close it after iteration */
  356. }
  357. else { /* open a new file */
  358. const char *filename = luaL_checkstring(L, 1);
  359. opencheck(L, filename, "r");
  360. lua_replace(L, 1); /* put file at index 1 */
  361. toclose = 1; /* close it after iteration */
  362. }
  363. aux_lines(L, toclose);
  364. return 1;
  365. }
  366. /*
  367. ** {======================================================
  368. ** READ
  369. ** =======================================================
  370. */
  371. /* maximum length of a numeral */
  372. #if !defined (L_MAXLENNUM)
  373. #define L_MAXLENNUM 200
  374. #endif
  375. /* auxiliary structure used by 'read_number' */
  376. typedef struct {
  377. FILE *f; /* file being read */
  378. int c; /* current character (look ahead) */
  379. int n; /* number of elements in buffer 'buff' */
  380. char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */
  381. } RN;
  382. /*
  383. ** Add current char to buffer (if not out of space) and read next one
  384. */
  385. // static int nextc (RN *rn) {
  386. // if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */
  387. // rn->buff[0] = '\0'; /* invalidate result */
  388. // return 0; /* fail */
  389. // }
  390. // else {
  391. // rn->buff[rn->n++] = rn->c; /* save current char */
  392. // rn->c = l_getc(rn->f); /* read next one */
  393. // return 1;
  394. // }
  395. // }
  396. /*
  397. ** Accept current char if it is in 'set' (of size 2)
  398. */
  399. // static int test2 (RN *rn, const char *set) {
  400. // if (rn->c == set[0] || rn->c == set[1])
  401. // return nextc(rn);
  402. // else return 0;
  403. // }
  404. /*
  405. ** Read a sequence of (hex)digits
  406. */
  407. // static int readdigits (RN *rn, int hex) {
  408. // int count = 0;
  409. // while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn))
  410. // count++;
  411. // return count;
  412. // }
  413. /*
  414. ** Read a number: first reads a valid prefix of a numeral into a buffer.
  415. ** Then it calls 'lua_stringtonumber' to check whether the format is
  416. ** correct and to convert it to a Lua number
  417. */
  418. // static int read_number (lua_State *L, FILE *f) {
  419. // RN rn;
  420. // int count = 0;
  421. // int hex = 0;
  422. // char decp[2];
  423. // rn.f = f; rn.n = 0;
  424. // decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */
  425. // decp[1] = '.'; /* always accept a dot */
  426. // l_lockfile(rn.f);
  427. // do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */
  428. // test2(&rn, "-+"); /* optional signal */
  429. // if (test2(&rn, "00")) {
  430. // if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */
  431. // else count = 1; /* count initial '0' as a valid digit */
  432. // }
  433. // count += readdigits(&rn, hex); /* integral part */
  434. // if (test2(&rn, decp)) /* decimal point? */
  435. // count += readdigits(&rn, hex); /* fractional part */
  436. // if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */
  437. // test2(&rn, "-+"); /* exponent signal */
  438. // readdigits(&rn, 0); /* exponent digits */
  439. // }
  440. // ungetc(rn.c, rn.f); /* unread look-ahead char */
  441. // l_unlockfile(rn.f);
  442. // rn.buff[rn.n] = '\0'; /* finish string */
  443. // if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */
  444. // return 1; /* ok */
  445. // else { /* invalid format */
  446. // lua_pushnil(L); /* "result" to be removed */
  447. // return 0; /* read fails */
  448. // }
  449. // }
  450. static int test_eof (lua_State *L, FILE *f) {
  451. return feof(f);
  452. // int c = getc(f);
  453. // ungetc(c, f); /* no-op when c == EOF */
  454. // lua_pushliteral(L, "");
  455. // return (c != EOF);
  456. }
  457. static int read_line (lua_State *L, FILE *f, int chop) {
  458. luaL_Buffer b;
  459. int c = '\0';
  460. #define READLINE_BUFF_SIZE (1024)
  461. luaL_buffinitsize(L, &b, READLINE_BUFF_SIZE);
  462. // luaL_buffinit(L, &b);
  463. while (c != EOF && c != '\n') { /* repeat until end of line */
  464. char *buff = luaL_prepbuffsize(&b, READLINE_BUFF_SIZE); /* preallocate buffer */
  465. int i = 0;
  466. l_lockfile(f); /* no memory errors can happen inside the lock */
  467. while (i < READLINE_BUFF_SIZE && (c = l_getc(f)) != EOF && c != '\n')
  468. buff[i++] = c;
  469. l_unlockfile(f);
  470. luaL_addsize(&b, i);
  471. }
  472. if (!chop && c == '\n') /* want a newline and have one? */
  473. luaL_addchar(&b, c); /* add ending newline to result */
  474. luaL_pushresult(&b); /* close buffer */
  475. /* return ok if read something (either a newline or something else) */
  476. return (c == '\n' || lua_rawlen(L, -1) > 0);
  477. }
  478. static void read_all (lua_State *L, FILE *f) {
  479. size_t nr;
  480. luaL_Buffer b;
  481. luaL_buffinit(L, &b);
  482. do { /* read file in chunks of LUAL_BUFFERSIZE bytes */
  483. char *p = luaL_prepbuffer(&b);
  484. nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f);
  485. luaL_addsize(&b, nr);
  486. } while (nr == LUAL_BUFFERSIZE);
  487. luaL_pushresult(&b); /* close buffer */
  488. }
  489. static int read_chars (lua_State *L, FILE *f, size_t n) {
  490. size_t nr; /* number of chars actually read */
  491. char *p;
  492. luaL_Buffer b;
  493. luaL_buffinit(L, &b);
  494. p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */
  495. nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */
  496. luaL_addsize(&b, nr);
  497. luaL_pushresult(&b); /* close buffer */
  498. return (nr > 0); /* true iff read something */
  499. }
  500. static int g_read (lua_State *L, FILE *f, int first) {
  501. int nargs = lua_gettop(L) - 1;
  502. int success;
  503. int n;
  504. //clearerr(f);
  505. if (nargs == 0) { /* no arguments? */
  506. success = read_line(L, f, 1);
  507. n = first+1; /* to return 1 result */
  508. }
  509. else { /* ensure stack space for all results and for auxlib's buffer */
  510. luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
  511. success = 1;
  512. for (n = first; nargs-- && success; n++) {
  513. if (lua_type(L, n) == LUA_TNUMBER) {
  514. size_t l = (size_t)luaL_checkinteger(L, n);
  515. success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
  516. }
  517. else {
  518. const char *p = luaL_checkstring(L, n);
  519. if (*p == '*') p++; /* skip optional '*' (for compatibility) */
  520. switch (*p) {
  521. // case 'n': /* number */
  522. // success = read_number(L, f);
  523. // break;
  524. case 'l': /* line */
  525. success = read_line(L, f, 1);
  526. break;
  527. case 'L': /* line with end-of-line */
  528. success = read_line(L, f, 0);
  529. break;
  530. case 'a': /* file */
  531. read_all(L, f); /* read entire file */
  532. success = 1; /* always success */
  533. break;
  534. default:
  535. return luaL_argerror(L, n, "invalid format");
  536. }
  537. }
  538. }
  539. }
  540. if (ferror(f))
  541. return luaL_fileresult(L, 0, NULL);
  542. if (!success) {
  543. lua_pop(L, 1); /* remove last result */
  544. lua_pushnil(L); /* push nil instead */
  545. }
  546. return n - first;
  547. }
  548. #ifdef LUA_USE_WINDOWS
  549. static int io_read (lua_State *L) {
  550. return g_read(L, getiofile(L, IO_INPUT), 1);
  551. }
  552. #endif
  553. static int f_read (lua_State *L) {
  554. return g_read(L, tofile(L), 2);
  555. }
  556. static int io_readline (lua_State *L) {
  557. LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1));
  558. int i;
  559. int n = (int)lua_tointeger(L, lua_upvalueindex(2));
  560. if (isclosed(p)) /* file is already closed? */
  561. return luaL_error(L, "file is already closed");
  562. lua_settop(L , 1);
  563. luaL_checkstack(L, n, "too many arguments");
  564. for (i = 1; i <= n; i++) /* push arguments to 'g_read' */
  565. lua_pushvalue(L, lua_upvalueindex(3 + i));
  566. n = g_read(L, p->f, 2); /* 'n' is number of results */
  567. lua_assert(n > 0); /* should return at least a nil */
  568. if (lua_toboolean(L, -n)) /* read at least one value? */
  569. return n; /* return them */
  570. else { /* first result is nil: EOF or error */
  571. if (n > 1) { /* is there error information? */
  572. /* 2nd result is error message */
  573. return luaL_error(L, "%s", lua_tostring(L, -n + 1));
  574. }
  575. if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */
  576. lua_settop(L, 0);
  577. lua_pushvalue(L, lua_upvalueindex(1));
  578. aux_close(L); /* close it */
  579. }
  580. return 0;
  581. }
  582. }
  583. /* }====================================================== */
  584. static int g_write (lua_State *L, FILE *f, int arg) {
  585. int nargs = lua_gettop(L) - arg;
  586. int status = 1;
  587. for (; nargs--; arg++) {
  588. // if (lua_type(L, arg) == LUA_TNUMBER) {
  589. // /* optimization: could be done exactly as for strings */
  590. // int len = lua_isinteger(L, arg)
  591. // ? fprintf(f, LUA_INTEGER_FMT,
  592. // (LUAI_UACINT)lua_tointeger(L, arg))
  593. // : fprintf(f, LUA_NUMBER_FMT,
  594. // (LUAI_UACNUMBER)lua_tonumber(L, arg));
  595. // status = status && (len > 0);
  596. // }
  597. // else {
  598. size_t l;
  599. const char *s = luaL_checklstring(L, arg, &l);
  600. status = status && (fwrite(s, sizeof(char), l, f) == l);
  601. // }
  602. }
  603. if (status) return 1; /* file handle already on stack top */
  604. else return luaL_fileresult(L, status, NULL);
  605. }
  606. #ifdef LUA_USE_WINDOWS
  607. static int io_write (lua_State *L) {
  608. return g_write(L, getiofile(L, IO_OUTPUT), 1);
  609. }
  610. #endif
  611. static int f_write (lua_State *L) {
  612. FILE *f = tofile(L);
  613. lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */
  614. return g_write(L, f, 2);
  615. }
  616. static int f_seek (lua_State *L) {
  617. static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
  618. static const char *const modenames[] = {"set", "cur", "end", NULL};
  619. FILE *f = tofile(L);
  620. int op = luaL_checkoption(L, 2, "cur", modenames);
  621. lua_Integer p3 = luaL_optinteger(L, 3, 0);
  622. l_seeknum offset = (l_seeknum)p3;
  623. luaL_argcheck(L, (lua_Integer)offset == p3, 3,
  624. "not an integer in proper range");
  625. op = l_fseek(f, offset, mode[op]);
  626. if (op)
  627. return luaL_fileresult(L, 0, NULL); /* error */
  628. else {
  629. lua_pushinteger(L, (lua_Integer)l_ftell(f));
  630. return 1;
  631. }
  632. }
  633. #ifdef LUA_USE_WINDOWS
  634. static int f_setvbuf (lua_State *L) {
  635. static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
  636. static const char *const modenames[] = {"no", "full", "line", NULL};
  637. FILE *f = tofile(L);
  638. int op = luaL_checkoption(L, 2, NULL, modenames);
  639. lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
  640. int res = setvbuf(f, NULL, mode[op], (size_t)sz);
  641. return luaL_fileresult(L, res == 0, NULL);
  642. }
  643. static int io_flush (lua_State *L) {
  644. return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
  645. }
  646. #endif
  647. static int f_flush (lua_State *L) {
  648. #ifdef LUA_USE_WINDOWS
  649. return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL);
  650. #else
  651. return 0;
  652. #endif
  653. }
  654. #include "luat_mem.h"
  655. /*
  656. 判断文件是否存在
  657. @api io.exists(path)
  658. @string 文件路径
  659. @return bool 存在返回true,否则返回false
  660. @usage
  661. log.info("io", "file exists", io.exists("/boottime"))
  662. */
  663. static int io_exists (lua_State *L) {
  664. const char *filename = luaL_checkstring(L, 1);
  665. FILE* f = fopen(filename, "r");
  666. lua_pushboolean(L,f != NULL);
  667. if(f!=NULL)
  668. fclose(f);
  669. return 1;
  670. }
  671. /*
  672. 获取文件大小
  673. @api io.fileSize(path)
  674. @string 文件路径
  675. @return int 文件数据,若文件不存在会返回nil
  676. @usage
  677. local fsize = io.fileSize("/bootime")
  678. if fsize and fsize > 1024 then
  679. log.info("io", "file size", fsize)
  680. end
  681. */
  682. static int io_fileSize (lua_State *L) {
  683. const char *filename = luaL_checkstring(L, 1);
  684. FILE* f = fopen(filename, "rb");
  685. if(f == NULL) {
  686. lua_pushinteger(L, 0);
  687. }
  688. else {
  689. fseek(f, 0, SEEK_END);
  690. lua_pushinteger(L,ftell(f));
  691. fclose(f);
  692. }
  693. return 1;
  694. }
  695. /**
  696. 读取整个文件,请注意内存消耗
  697. @api io.readFile(path, mode, offset, len)
  698. @string 文件路径
  699. @string 读取模式, 默认 "rb"
  700. @int 起始位置,默认0
  701. @int 读取长度,默认整个文件
  702. @return string 文件数据,若文件不存在会返回nil
  703. @usage
  704. local data = io.readFile("/bootime")
  705. -- 注意: offset和len参数是 2023.6.6添加的
  706. -- 读取abc.txt, 先跳过128字节, 然后读取512字节数据
  707. local data = io.readFile("/abc.txt", "rb", 128, 512)
  708. */
  709. static int io_readFile (lua_State *L) {
  710. const char *filename = luaL_checkstring(L, 1);
  711. const char *mode = luaL_optstring(L, 2, "rb");
  712. int offset = luaL_optinteger(L, 3, 0);
  713. int rlen = luaL_optinteger(L, 4, 1 << 30);
  714. FILE* f = fopen(filename, mode);
  715. if(f == NULL)
  716. return 0;
  717. char buff[512];
  718. luaL_Buffer b;
  719. luaL_buffinit(L, &b);
  720. int ret = 0;
  721. if (offset > 0) {
  722. luat_fs_fseek(f, offset, SEEK_SET);
  723. }
  724. while (rlen > 0) {
  725. if (rlen > 512)
  726. ret = fread(buff, 1, 512, f);
  727. else
  728. ret = fread(buff, 1, rlen, f);
  729. if (ret < 1)
  730. break;
  731. luaL_addlstring(&b, (const char*)buff, ret);
  732. rlen -= ret;
  733. }
  734. fclose(f);
  735. luaL_pushresult(&b);
  736. return 1;
  737. }
  738. /**
  739. 将数据写入文件
  740. @api io.writeFile(path, data)
  741. @string 文件路径
  742. @string 数据
  743. @return boolean 成功返回true, 否则返回false
  744. @usage
  745. io.writeFile("/bootime", "1")
  746. */
  747. static int io_writeFile (lua_State *L) {
  748. const char *filename = luaL_checkstring(L, 1);
  749. size_t len;
  750. const char *data = luaL_checklstring(L, 2, &len);
  751. const char *mode = luaL_optstring(L, 3, "wb+");
  752. FILE* f = fopen(filename, mode);
  753. if(f == NULL)
  754. return 0;
  755. fwrite(data, 1 , len, f);
  756. fclose(f);
  757. lua_pushboolean(L,1);
  758. return 1;
  759. }
  760. #ifdef LUAT_USE_ZBUFF
  761. #include "luat_zbuff.h"
  762. /*
  763. 读取文件并填充到zbuff内,但不移动指针位置
  764. @api io.fill(buff, offset, len)
  765. @userdata zbuff实体
  766. @int 写入的位置,默认是0
  767. @int 写入的长度,默认是zbuff的len减去offset
  768. @return boolean 成功返回true,否则返回false
  769. @return int 返回实际读取到的长度,如果小于0也说明是读取失败了
  770. @usage
  771. local buff = zbuff.create(1024)
  772. local f = io.open("/sd/test.txt")
  773. if f then
  774. f:fill(buff)
  775. end
  776. */
  777. static int f_fill(lua_State *L) {
  778. FILE* f = tofile(L);
  779. luat_zbuff_t* buff;
  780. int offset;
  781. int len;
  782. if (!lua_isuserdata(L, 2)) {
  783. return 0;
  784. }
  785. if (f == NULL)
  786. return 0;
  787. buff = luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE);
  788. if (lua_isinteger(L, 3)) {
  789. offset = luaL_checkinteger(L, 3);
  790. }
  791. else {
  792. offset = 0;
  793. }
  794. if (lua_isinteger(L, 4)) {
  795. len = luaL_checkinteger(L, 4);
  796. if (len > buff->len)
  797. len = buff->len;
  798. if (offset + len > buff->len)
  799. len = len - offset;
  800. }
  801. else {
  802. len = buff->len - offset;
  803. }
  804. len = fread(buff->addr + offset, 1, len, f);
  805. lua_pushboolean(L, len >= 0 ? 1 : 0);
  806. lua_pushinteger(L, len);
  807. return 2;
  808. }
  809. #endif
  810. /*
  811. 获取文件系统信息
  812. @api io.fsstat(path)
  813. @string 路径,默认"/",可选
  814. @return boolean 获取成功返回true,否则返回false
  815. @return int 总的block数量
  816. @return int 已使用的block数量
  817. @return int block的大小,单位字节
  818. @return string 文件系统类型,例如lfs代表littlefs
  819. @usage
  820. -- 打印根分区的信息
  821. log.info("fsstat", io.fsstat("/"))
  822. */
  823. static int l_fs_fsstat(lua_State *L) {
  824. const char* path = luaL_optstring(L, 1, "/");
  825. luat_fs_info_t info = {0};
  826. if (luat_fs_info(path, &info) == 0) {
  827. lua_pushboolean(L, 1);
  828. lua_pushinteger(L, info.total_block);
  829. lua_pushinteger(L, info.block_used);
  830. lua_pushinteger(L, info.block_size);
  831. lua_pushstring(L, info.filesystem);
  832. return 5;
  833. } else {
  834. lua_pushboolean(L, 0);
  835. return 1;
  836. }
  837. }
  838. /*
  839. 判断目录是否存在
  840. @api io.dexist(path)
  841. @string 目录路径
  842. @return bool 存在返回true,否则返回false
  843. @usage
  844. -- 本函数于2025.8.12新增
  845. log.info("io", "dir存在吗?", io.dexist("/sd/myf"))
  846. */
  847. static int io_dexist (lua_State *L) {
  848. const char *dirname = luaL_checkstring(L, 1);
  849. int ret = luat_fs_dexist(dirname);
  850. lua_pushboolean(L,ret?1:0);
  851. return 1;
  852. }
  853. static int io_mkfs (lua_State *L);
  854. static int io_mkdir (lua_State *L);
  855. static int io_rmdir (lua_State *L);
  856. static int io_lsdir (lua_State *L);
  857. static int io_lsmount (lua_State *L);
  858. /*
  859. ** functions for 'io' library
  860. */
  861. #include "rotable2.h"
  862. static const rotable_Reg_t iolib[] = {
  863. {"open", ROREG_FUNC(io_open)},
  864. #ifdef LUA_USE_WINDOWS
  865. {"popen", ROREG_FUNC(io_popen)},
  866. {"read", ROREG_FUNC(io_read)},
  867. {"tmpfile", ROREG_FUNC(io_tmpfile)},
  868. {"write", ROREG_FUNC(io_write)},
  869. {"close", ROREG_FUNC(io_close)},
  870. {"flush", ROREG_FUNC(io_flush)},
  871. {"input", ROREG_FUNC(io_input)},
  872. {"output", ROREG_FUNC(io_output)},
  873. #endif
  874. {"type", ROREG_FUNC(io_type)},
  875. {"exists", ROREG_FUNC(io_exists)},
  876. {"fileSize", ROREG_FUNC(io_fileSize)},
  877. {"readFile", ROREG_FUNC(io_readFile)},
  878. {"writeFile", ROREG_FUNC(io_writeFile)},
  879. {"lines", ROREG_FUNC(io_lines)},
  880. {"mkdir", ROREG_FUNC(io_mkdir)},
  881. {"rmdir", ROREG_FUNC(io_rmdir)},
  882. {"lsdir", ROREG_FUNC(io_lsdir)},
  883. {"mkfs", ROREG_FUNC(io_mkfs)},
  884. {"lsmount", ROREG_FUNC(io_lsmount)},
  885. {"dexist", ROREG_FUNC(io_dexist)},
  886. // 从fs库迁移函数
  887. { "fsstat", ROREG_FUNC(l_fs_fsstat)},
  888. { "fsize", ROREG_FUNC(io_fileSize )},
  889. {"FILE", ROREG_INT(0)},
  890. {"DIR", ROREG_INT(1)},
  891. {NULL, ROREG_INT(0) }
  892. };
  893. /*
  894. ** methods for file handles
  895. */
  896. static const luaL_Reg flib[] = {
  897. {"close", f_close},
  898. {"flush", f_flush},
  899. {"lines", f_lines},
  900. {"read", f_read},
  901. {"seek", f_seek},
  902. #ifdef LUA_USE_WINDOWS
  903. {"setvbuf", f_setvbuf},
  904. #endif
  905. {"write", f_write},
  906. #ifdef LUAT_USE_ZBUFF
  907. {"fill", f_fill},
  908. #endif
  909. {"__gc", f_gc},
  910. {"__tostring", f_tostring},
  911. {NULL, NULL}
  912. };
  913. static int luat_io_meta_index(lua_State *L) {
  914. if (lua_isstring(L, 2)) {
  915. const char* keyname = luaL_checkstring(L, 2);
  916. //printf("zbuff keyname = %s\n", keyname);
  917. int i = 0;
  918. while (1) {
  919. if (flib[i].name == NULL) break;
  920. if (!strcmp(keyname, flib[i].name)) {
  921. lua_pushcfunction(L, flib[i].func);
  922. return 1;
  923. }
  924. i++;
  925. }
  926. }
  927. return 0;
  928. }
  929. static void createmeta (lua_State *L) {
  930. luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
  931. //lua_pushvalue(L, -1); /* push metatable */
  932. lua_pushcfunction(L, luat_io_meta_index);
  933. lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
  934. //luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */
  935. lua_pop(L, 1); /* pop new metatable */
  936. //lua_pushvalue(L, -1);
  937. //lua_newtable( L );
  938. //rotable_newidx( L, flib);
  939. //lua_setfield( L, -2, "__index" );
  940. //lua_setmetatable( L, -2 );
  941. //lua_pop(L, 1);
  942. }
  943. /*
  944. ** function to (not) close the standard files stdin, stdout, and stderr
  945. */
  946. // static int io_noclose (lua_State *L) {
  947. // LStream *p = tolstream(L);
  948. // p->closef = &io_noclose; /* keep file opened */
  949. // lua_pushnil(L);
  950. // lua_pushliteral(L, "cannot close standard file");
  951. // return 2;
  952. // }
  953. // static void createstdfile (lua_State *L, FILE *f, const char *k,
  954. // const char *fname) {
  955. // LStream *p = newprefile(L);
  956. // p->f = f;
  957. // p->closef = &io_noclose;
  958. // if (k != NULL) {
  959. // lua_pushvalue(L, -1);
  960. // lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */
  961. // }
  962. // lua_setfield(L, -2, fname); /* add file to module */
  963. // }
  964. LUAMOD_API int luaopen_io (lua_State *L) {
  965. luat_newlib2(L, iolib); /* new module */
  966. createmeta(L);
  967. /* create (and set) default files */
  968. //createstdfile(L, stdin, IO_INPUT, "stdin");
  969. //createstdfile(L, stdout, IO_OUTPUT, "stdout");
  970. //createstdfile(L, stderr, NULL, "stderr");
  971. return 1;
  972. }
  973. /*
  974. 格式化文件系统,需指定挂载点
  975. @api io.mkfs(path)
  976. @string 挂载点
  977. @return bool 成功与否
  978. @return int 底层返回值
  979. @usage
  980. local ret, errio = io.mkfs("/sd")
  981. log.info("fs", "mkfs", ret, errio)
  982. */
  983. static int io_mkfs (lua_State *L) {
  984. luat_fs_conf_t conf = {0};
  985. conf.mount_point = (char*)luaL_checkstring(L, 1);
  986. int ret = luat_fs_mkfs(&conf);
  987. lua_pushboolean(L, ret == 0 ? 1 : 0);
  988. lua_pushinteger(L, ret);
  989. return 2;
  990. }
  991. /*
  992. 创建文件夹
  993. @api io.mkdir(path)
  994. @string 需要建立的目录路径
  995. @return bool 成功与否
  996. @return int 底层返回值
  997. @usage
  998. local ret, errio = io.mkdir("/data/")
  999. log.info("fs", "mkdir", ret, errio)
  1000. */
  1001. static int io_mkdir (lua_State *L) {
  1002. const char* path = luaL_checkstring(L, 1);
  1003. int ret = luat_fs_mkdir(path);
  1004. lua_pushboolean(L, ret == 0 ? 1 : 0);
  1005. lua_pushinteger(L, ret);
  1006. return 2;
  1007. }
  1008. /*
  1009. 删除文件夹
  1010. @api io.rmdir(path)
  1011. @string 需要移除的目录路径
  1012. @return bool 成功与否
  1013. @return int 底层返回值
  1014. @usage
  1015. local ret, errio = io.rmdir("/data/")
  1016. log.info("fs", "rmdir", ret, errio)
  1017. */
  1018. static int io_rmdir (lua_State *L) {
  1019. const char* path = luaL_checkstring(L, 1);
  1020. int ret = luat_fs_rmdir(path);
  1021. lua_pushboolean(L, ret == 0 ? 1 : 0);
  1022. lua_pushinteger(L, ret);
  1023. return 2;
  1024. }
  1025. /*
  1026. 列出目录下的文件
  1027. @api io.lsdir(path, len, offset)
  1028. @string 需要枚举的目录路径
  1029. @int 最大长度, 默认10, 最高50
  1030. @int 偏移量, 默认0, 当目录文件很多时分页查询用
  1031. @return bool 成功与否
  1032. @return int 底层返回值
  1033. @usage
  1034. local ret, data = io.lsdir("/data/", 10, 0)
  1035. if ret then
  1036. log.info("fs", "lsdir", json.encode(data))
  1037. else
  1038. log.info("fs", "lsdir", "fail", ret, data)
  1039. end
  1040. */
  1041. static int io_lsdir (lua_State *L) {
  1042. const char* path = luaL_checkstring(L, 1);
  1043. int len = luaL_optinteger(L, 2, 10);
  1044. int offset = luaL_optinteger(L, 3, 0);
  1045. if (len < 0) {
  1046. len = 10;
  1047. } else if (len > 100) {
  1048. len = 100;
  1049. }
  1050. if (offset < 0)
  1051. offset = 0;
  1052. luat_fs_dirent_t* ents = luat_heap_malloc(sizeof(luat_fs_dirent_t) * len);
  1053. if (ents == NULL) {
  1054. LLOGE("out of memory when malloc luat_fs_dirent_t");
  1055. return 0;
  1056. }
  1057. int ret = luat_fs_lsdir(path, ents, offset, len);
  1058. //LLOGD("luat_fs_lsdir ret %d", ret);
  1059. if (ret == 0) {
  1060. luat_heap_free(ents);
  1061. lua_pushboolean(L, 1);
  1062. lua_newtable(L);
  1063. return 2;
  1064. }
  1065. else if (ret > 0) {
  1066. lua_pushboolean(L, 1);
  1067. lua_createtable(L, ret, 0);
  1068. for (size_t i = 0; i < ret; i++)
  1069. {
  1070. lua_createtable(L, 0, 3);
  1071. lua_pushinteger(L, ents[i].d_type);
  1072. lua_setfield(L, -2, "type");
  1073. lua_pushstring(L, ents[i].d_name);
  1074. lua_setfield(L, -2, "name");
  1075. lua_pushinteger(L, ents[i].d_size);
  1076. lua_setfield(L, -2, "size");
  1077. lua_seti(L, -2, i + 1);
  1078. }
  1079. luat_heap_free(ents);
  1080. return 2;
  1081. }
  1082. else {
  1083. luat_heap_free(ents);
  1084. lua_pushboolean(L, 0);
  1085. lua_pushinteger(L, ret);
  1086. return 2;
  1087. }
  1088. return 0;
  1089. }
  1090. /*
  1091. 列出所有挂载点
  1092. @api io.lsmount()
  1093. @return table 挂载点列表
  1094. @usage
  1095. local data = io.lsmount()
  1096. log.info("fs", "lsmount", json.encode(data))
  1097. */
  1098. #ifdef LUAT_USE_FS_VFS
  1099. luat_vfs_t* luat_vfs_self(void);
  1100. #endif
  1101. static int io_lsmount (lua_State *L) {
  1102. lua_newtable(L);
  1103. #ifdef LUAT_USE_FS_VFS
  1104. luat_vfs_t* vfs = luat_vfs_self();
  1105. for (size_t j = 0; j < LUAT_VFS_FILESYSTEM_MOUNT_MAX; j++) {
  1106. if (vfs->mounted[j].ok == 0)
  1107. continue;
  1108. lua_newtable(L);
  1109. lua_pushstring(L, vfs->mounted[j].prefix);
  1110. lua_setfield(L, -2, "path");
  1111. lua_pushstring(L, vfs->mounted[j].fs->name);
  1112. lua_setfield(L, -2, "fs");
  1113. lua_seti(L, -2, j+1);
  1114. }
  1115. #else
  1116. lua_newtable(L);
  1117. lua_pushliteral(L, "");
  1118. lua_setfield(L, -2, "path");
  1119. lua_pushliteral(L, "posix");
  1120. lua_setfield(L, -2, "fs");
  1121. lua_seti(L, -2, 1);
  1122. #endif
  1123. return 1;
  1124. }