fs.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. // Filesystem module
  2. // Copyright (C) 2015 Legimet
  3. //
  4. // This file is part of Duktape-nspire.
  5. //
  6. // Duktape-nspire is free software: you can redistribute it and/or modify
  7. // it under the terms of the GNU Lesser General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // Duktape-nspire is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Lesser General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Lesser General Public License
  17. // along with Duktape-nspire. If not, see <http://www.gnu.org/licenses/>.
  18. #include "duktape.h"
  19. #include <errno.h>
  20. #include <stdbool.h>
  21. #include <string.h>
  22. #include <stdio.h>
  23. #include <sys/types.h>
  24. #include <sys/stat.h>
  25. #include <unistd.h>
  26. #include <dirent.h>
  27. #include <fcntl.h>
  28. #define FS_SET_STATS_INT_PROP(ctx, buf, idx, prop) \
  29. do { \
  30. duk_push_int((ctx), (buf)->st_##prop); \
  31. duk_put_prop_string((ctx), (idx), #prop); \
  32. } while (0)
  33. #define FS_SET_STATS_DATE_PROP(ctx, buf, idx1, idx2, prop) \
  34. do { \
  35. duk_get_prop_string((ctx), (idx1), "Date"); \
  36. duk_push_int((ctx), buf->st_##prop); \
  37. duk_new((ctx), 1); \
  38. duk_put_prop_string((ctx), (idx2), #prop); \
  39. } while (0)
  40. static duk_ret_t fs_err(duk_context *ctx, bool success, duk_idx_t callback, int callback_args) {
  41. if (duk_is_function(ctx, callback)) {
  42. duk_dup(ctx, callback);
  43. duk_insert(ctx, -callback_args);
  44. }
  45. if (!success) {
  46. duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", strerror(errno));
  47. if (duk_is_function(ctx, callback)) {
  48. duk_insert(ctx, -callback_args);
  49. }
  50. }
  51. if (duk_is_function(ctx, callback)) {
  52. if (success) {
  53. duk_push_null(ctx);
  54. if (duk_is_function(ctx, callback)) {
  55. duk_insert(ctx, -callback_args);
  56. }
  57. }
  58. duk_call(ctx, callback_args);
  59. } else if (!success) {
  60. return duk_throw(ctx);
  61. }
  62. if (callback_args) {
  63. return 0;
  64. } else {
  65. return 1;
  66. }
  67. }
  68. static duk_ret_t fs_rename(duk_context *ctx) {
  69. bool success = !rename(duk_require_string(ctx, 0), duk_require_string(ctx, 1));
  70. return fs_err(ctx, success, 2, 1);
  71. }
  72. static void fs_init_stats_obj(duk_context *ctx, struct stat *buf) {
  73. duk_push_this(ctx);
  74. duk_get_prop_string(ctx, -1, "Stats");
  75. duk_new(ctx, 0);
  76. FS_SET_STATS_INT_PROP(ctx, buf, -2, dev);
  77. FS_SET_STATS_INT_PROP(ctx, buf, -2, ino);
  78. FS_SET_STATS_INT_PROP(ctx, buf, -2, mode);
  79. FS_SET_STATS_INT_PROP(ctx, buf, -2, nlink);
  80. FS_SET_STATS_INT_PROP(ctx, buf, -2, uid);
  81. FS_SET_STATS_INT_PROP(ctx, buf, -2, gid);
  82. FS_SET_STATS_INT_PROP(ctx, buf, -2, rdev);
  83. FS_SET_STATS_INT_PROP(ctx, buf, -2, size);
  84. FS_SET_STATS_INT_PROP(ctx, buf, -2, blksize);
  85. FS_SET_STATS_INT_PROP(ctx, buf, -2, blocks);
  86. duk_push_global_object(ctx);
  87. FS_SET_STATS_DATE_PROP(ctx, buf, -1, -3, atime);
  88. FS_SET_STATS_DATE_PROP(ctx, buf, -1, -3, mtime);
  89. FS_SET_STATS_DATE_PROP(ctx, buf, -1, -3, ctime);
  90. duk_swap(ctx, -2, -3);
  91. duk_pop_2(ctx);
  92. }
  93. static duk_ret_t fs_stat(duk_context *ctx) {
  94. struct stat buf;
  95. bool success = !stat(duk_require_string(ctx, 0), &buf);
  96. if (success) {
  97. fs_init_stats_obj(ctx, &buf);
  98. } else {
  99. duk_push_undefined(ctx);
  100. }
  101. return fs_err(ctx, success, 1, 2);
  102. }
  103. static duk_ret_t fs_stat_sync(duk_context *ctx) {
  104. struct stat buf;
  105. bool success = !stat(duk_require_string(ctx, 0), &buf);
  106. if (success) {
  107. fs_init_stats_obj(ctx, &buf);
  108. }
  109. return fs_err(ctx, success, DUK_INVALID_INDEX, 0);
  110. }
  111. static duk_ret_t fs_unlink(duk_context *ctx) {
  112. bool success = !unlink(duk_require_string(ctx, 0));
  113. return fs_err(ctx, success, 1, 1);
  114. }
  115. static duk_ret_t fs_rmdir(duk_context *ctx) {
  116. bool success = !rmdir(duk_require_string(ctx, 0));
  117. return fs_err(ctx, success, 1, 1);
  118. }
  119. static duk_ret_t fs_mkdir(duk_context *ctx) {
  120. bool success;
  121. if (duk_is_number(ctx, 1)) {
  122. success = !mkdir(duk_require_string(ctx, 0), duk_require_int(ctx, 1));
  123. return fs_err(ctx, success, 2, 1);
  124. } else {
  125. success = !mkdir(duk_require_string(ctx, 0), 0777);
  126. return fs_err(ctx, success, 1, 1);
  127. }
  128. }
  129. static bool fs_init_readdir_arr(duk_context *ctx) {
  130. DIR *pdir;
  131. struct dirent *pdirent;
  132. bool success;
  133. pdir = opendir(duk_require_string(ctx, 0));
  134. if (pdir) {
  135. duk_idx_t obj_idx = duk_push_array(ctx);
  136. duk_uarridx_t idx = 0;
  137. errno = 0;
  138. do {
  139. errno = 0;
  140. if ((pdirent = readdir(pdir)) && strcmp(pdirent->d_name, ".") && strcmp(pdirent->d_name, "..")) {
  141. duk_push_string(ctx, pdirent->d_name);
  142. duk_put_prop_index(ctx, obj_idx, idx);
  143. }
  144. idx++;
  145. } while (pdirent != NULL);
  146. if (errno) {
  147. int e = errno;
  148. closedir(pdir);
  149. errno = e;
  150. success = false;
  151. duk_pop(ctx);
  152. } else {
  153. closedir(pdir);
  154. success = true;
  155. }
  156. } else {
  157. success = false;
  158. }
  159. return success;
  160. }
  161. static duk_ret_t fs_readdir(duk_context *ctx) {
  162. return fs_err(ctx, fs_init_readdir_arr(ctx), 1, 2);
  163. }
  164. static duk_ret_t fs_readdir_sync(duk_context *ctx) {
  165. return fs_err(ctx, fs_init_readdir_arr(ctx), DUK_INVALID_INDEX, 0);
  166. }
  167. static duk_ret_t fs_close(duk_context *ctx) {
  168. bool success = !close(duk_require_int(ctx, 0));
  169. return fs_err(ctx, success, 1, 1);
  170. }
  171. static bool fs_open_get_flags(const char *s, int *flag) {
  172. int flag1 = 0;
  173. int flag2 = 0;
  174. switch (s[0]) {
  175. case 'r':
  176. flag1 = O_RDONLY;
  177. break;
  178. case 'w':
  179. flag1 = O_WRONLY;
  180. flag2 = O_CREAT | O_TRUNC;
  181. break;
  182. case 'a':
  183. flag1 = O_WRONLY;
  184. flag2 = O_APPEND | O_TRUNC;
  185. break;
  186. default:
  187. return false;
  188. }
  189. switch (s[1]) {
  190. case '\0':
  191. break;
  192. case '+':
  193. if (s[2])
  194. return false;
  195. flag1 = O_RDWR;
  196. break;
  197. case 's':
  198. if (flag1 != O_RDONLY || (s[2] != '\0' && s[2] != '+') ||
  199. (s[2] == '+' && s[3]))
  200. return false;
  201. if (s[2] == '+')
  202. flag1 = O_RDWR;
  203. break;
  204. case 'x':
  205. if (flag1 != O_WRONLY || (s[2] != '\0' && s[2] != '+') ||
  206. (s[2] == '+' && s[3]))
  207. return false;
  208. flag2 |= O_EXCL;
  209. if (s[2] == '+')
  210. flag1 = O_RDWR;
  211. break;
  212. default:
  213. return false;
  214. }
  215. *flag = flag1 | flag2;
  216. return true;
  217. }
  218. static duk_ret_t fs_open(duk_context *ctx) {
  219. int fd;
  220. const char *path = duk_require_string(ctx, 0);
  221. const char *flags_str = duk_require_string(ctx, 1);
  222. int flags;
  223. if (!fs_open_get_flags(flags_str, &flags))
  224. return duk_error(ctx, DUK_ERR_ERROR, "Unknown file open flag: %s", flags_str);
  225. mode_t mode;
  226. duk_idx_t callback;
  227. if (duk_is_number(ctx, 2)) {
  228. mode = duk_get_int(ctx, 2);
  229. callback = 3;
  230. } else {
  231. mode = 0666;
  232. callback = 2;
  233. }
  234. fd = open(path, flags, mode);
  235. if (fd != -1) {
  236. duk_push_int(ctx, fd);
  237. } else {
  238. duk_push_undefined(ctx);
  239. }
  240. return fs_err(ctx, fd != -1, callback, 2);
  241. }
  242. static duk_ret_t fs_open_sync(duk_context *ctx) {
  243. int fd;
  244. const char *path = duk_require_string(ctx, 0);
  245. const char *flags_str = duk_require_string(ctx, 1);
  246. int flags;
  247. if (!fs_open_get_flags(flags_str, &flags))
  248. return duk_error(ctx, DUK_ERR_ERROR, "Unknown file open flag: %s", flags_str);
  249. mode_t mode;
  250. if (duk_is_number(ctx, 2)) {
  251. mode = duk_get_int(ctx, 2);
  252. } else {
  253. mode = 0666;
  254. }
  255. fd = open(path, flags, mode);
  256. if (fd != -1) {
  257. duk_push_int(ctx, fd);
  258. }
  259. return fs_err(ctx, fd != -1, DUK_INVALID_INDEX, 0);
  260. }
  261. static duk_ret_t fs_stats_check_mode(duk_context *ctx, mode_t mask) {
  262. duk_push_this(ctx);
  263. duk_get_prop_string(ctx, -1, "mode");
  264. if (duk_is_undefined(ctx, -1)) {
  265. duk_push_false(ctx);
  266. } else {
  267. if ((duk_get_int(ctx, -1) & S_IFMT) == mask) {
  268. duk_push_true(ctx);
  269. } else {
  270. duk_push_false(ctx);
  271. }
  272. }
  273. return 1;
  274. }
  275. static duk_ret_t fs_stats_is_file(duk_context *ctx) {
  276. return fs_stats_check_mode(ctx, S_IFREG);
  277. }
  278. static duk_ret_t fs_stats_is_dir(duk_context *ctx) {
  279. return fs_stats_check_mode(ctx, S_IFDIR);
  280. }
  281. static duk_ret_t fs_stats_is_blkdev(duk_context *ctx) {
  282. return fs_stats_check_mode(ctx, S_IFBLK);
  283. }
  284. static duk_ret_t fs_stats_is_chardev(duk_context *ctx) {
  285. return fs_stats_check_mode(ctx, S_IFCHR);
  286. }
  287. static duk_ret_t fs_stats_is_symlink(duk_context *ctx) {
  288. return fs_stats_check_mode(ctx, S_IFLNK);
  289. }
  290. static duk_ret_t fs_stats_isfifo(duk_context *ctx) {
  291. return fs_stats_check_mode(ctx, S_IFIFO);
  292. }
  293. static duk_ret_t fs_stats_issocket(duk_context *ctx) {
  294. return fs_stats_check_mode(ctx, S_IFSOCK);
  295. }
  296. static const duk_function_list_entry fs_stats_methods[] = {
  297. {"isFile", fs_stats_is_file, 0},
  298. {"isDirectory", fs_stats_is_dir, 0},
  299. {"isBlockDevice", fs_stats_is_blkdev, 0},
  300. {"isCharacterDevice", fs_stats_is_chardev, 0},
  301. {"isSymbolicLink", fs_stats_is_symlink, 0},
  302. {"isFIFO", fs_stats_isfifo, 0},
  303. {"isSocket", fs_stats_issocket, 0},
  304. {NULL, NULL, 0}
  305. };
  306. static duk_ret_t fs_stats_constructor(__attribute__((unused)) duk_context *ctx) {
  307. return 0;
  308. }
  309. static const duk_function_list_entry fs_funcs[] = {
  310. {"rename", fs_rename, 3},
  311. {"renameSync", fs_rename, 2},
  312. {"stat", fs_stat, 2},
  313. {"lstat", fs_stat, 2},
  314. {"statSync", fs_stat_sync, 1},
  315. {"lstatSync", fs_stat_sync, 1},
  316. {"unlink", fs_unlink, 2},
  317. {"unlinkSync", fs_unlink, 1},
  318. {"rmdir", fs_rmdir, 2},
  319. {"rmdirSync", fs_rmdir, 1},
  320. {"mkdir", fs_mkdir, DUK_VARARGS},
  321. {"mkdirSync", fs_mkdir, DUK_VARARGS},
  322. {"readdir", fs_readdir, 2},
  323. {"readdirSync", fs_readdir_sync, 1},
  324. {"close", fs_close, 2},
  325. {"closeSync", fs_close, 1},
  326. {"open", fs_open, DUK_VARARGS},
  327. {"openSync", fs_open_sync, DUK_VARARGS},
  328. {NULL, NULL, 0}
  329. };
  330. duk_ret_t dukopen_fs(duk_context *ctx) {
  331. duk_idx_t idx = duk_push_object(ctx);
  332. duk_put_function_list(ctx, idx, fs_funcs);
  333. duk_idx_t stats_constr = duk_push_c_function(ctx, fs_stats_constructor, 0);
  334. duk_idx_t stats_prot = duk_push_object(ctx);
  335. duk_put_function_list(ctx, stats_prot, fs_stats_methods);
  336. duk_put_prop_string(ctx, stats_constr, "prototype");
  337. duk_put_prop_string(ctx, idx, "Stats");
  338. return 1;
  339. }