MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 4835 lines 161 kB view raw
1#include <compat.h> // IWYU pragma: keep 2 3#include <uv.h> 4#include <stdio.h> 5#include <stdlib.h> 6#include <string.h> 7#include <stdbool.h> 8#include <sys/stat.h> 9#include <fcntl.h> 10#include <uthash.h> 11#include <utarray.h> 12#include <errno.h> 13 14#include "ant.h" 15#include "ptr.h" 16#include "utf8.h" 17#include "utils.h" 18#include "base64.h" 19#include "errors.h" 20#include "watch.h" 21#include "internal.h" 22#include "runtime.h" 23#include "descriptors.h" 24 25#include "gc/roots.h" 26#include "gc/modules.h" 27#include "silver/engine.h" 28 29#include "modules/fs.h" 30#include "modules/date.h" 31#include "modules/buffer.h" 32#include "modules/events.h" 33#include "modules/stream.h" 34#include "modules/symbol.h" 35#include "modules/url.h" 36 37typedef enum { 38 FS_ENC_NONE = 0, 39 FS_ENC_UTF8, 40 FS_ENC_UTF16LE, 41 FS_ENC_LATIN1, 42 FS_ENC_BASE64, 43 FS_ENC_BASE64URL, 44 FS_ENC_HEX, 45 FS_ENC_ASCII, 46} fs_encoding_t; 47 48typedef enum { 49 FS_OP_READ, 50 FS_OP_WRITE, 51 FS_OP_UNLINK, 52 FS_OP_MKDIR, 53 FS_OP_RMDIR, 54 FS_OP_STAT, 55 FS_OP_READ_BYTES, 56 FS_OP_EXISTS, 57 FS_OP_READDIR, 58 FS_OP_ACCESS, 59 FS_OP_REALPATH, 60 FS_OP_WRITE_FD, 61 FS_OP_READ_FD, 62 FS_OP_OPEN, 63 FS_OP_CLOSE, 64 FS_OP_MKDTEMP, 65 FS_OP_CHMOD, 66 FS_OP_RENAME 67} fs_op_type_t; 68 69typedef struct fs_request_s { 70 uv_fs_t uv_req; 71 ant_t *js; 72 ant_value_t promise; 73 ant_value_t target_buffer; 74 ant_value_t callback_fn; 75 76 char *path; 77 char *path2; 78 char *data; 79 char *error_msg; 80 size_t data_len; 81 size_t buf_offset; 82 83 fs_op_type_t op_type; 84 fs_encoding_t encoding; 85 uv_file fd; 86 87 int completed; 88 int failed; 89 int recursive; 90 int error_code; 91 int with_file_types; 92} fs_request_t; 93 94typedef enum { 95 FS_WATCH_MODE_EVENT = 0, 96 FS_WATCH_MODE_STAT 97} fs_watch_mode_t; 98 99typedef union { 100 uv_fs_event_t event; 101 uv_fs_poll_t poll; 102} fs_watcher_handle_t; 103 104typedef struct fs_watcher_s { 105 ant_t *js; 106 ant_value_t obj; 107 ant_value_t callback; 108 fs_watcher_handle_t handle; 109 uv_stat_t last_stat; 110 111 struct fs_watcher_s *next_active; 112 char *path; 113 114 bool last_stat_valid; 115 bool in_active_list; 116 bool closing; 117 bool handle_closed; 118 bool finalized; 119 bool persistent; 120 121 fs_watch_mode_t mode; 122} fs_watcher_t; 123 124typedef struct { 125 bool persistent; 126 bool recursive; 127 ant_value_t listener; 128} fs_watch_options_t; 129 130typedef struct { 131 bool persistent; 132 unsigned int interval_ms; 133 ant_value_t listener; 134} fs_watchfile_options_t; 135 136typedef struct { 137 mode_t mode; 138 double size, uid, gid; 139 double atime_ms, mtime_ms, ctime_ms, birthtime_ms; 140} fs_stat_fields_t; 141 142static ant_value_t g_dirent_proto = 0; 143static ant_value_t g_fswatcher_proto = 0; 144static ant_value_t g_fswatcher_ctor = 0; 145static ant_value_t g_filehandle_proto = 0; 146static ant_value_t g_readstream_proto = 0; 147static ant_value_t g_readstream_ctor = 0; 148static ant_value_t g_writestream_proto = 0; 149static ant_value_t g_writestream_ctor = 0; 150 151static fs_watcher_t *active_watchers = NULL; 152static UT_array *pending_requests = NULL; 153 154enum { 155 FS_WATCHER_NATIVE_TAG = 0x46535754u, // FSWT 156 FS_FILEHANDLE_NATIVE_TAG = 0x46534648u // FSFH 157}; 158 159static fs_watcher_t *fs_watcher_data(ant_value_t value) { 160 if (!js_check_native_tag(value, FS_WATCHER_NATIVE_TAG)) return NULL; 161 return (fs_watcher_t *)js_get_native_ptr(value); 162} 163 164static void fs_add_active_watcher(fs_watcher_t *watcher) { 165 if (!watcher || watcher->in_active_list) return; 166 watcher->next_active = active_watchers; 167 active_watchers = watcher; 168 watcher->in_active_list = true; 169} 170 171static void fs_remove_active_watcher(fs_watcher_t *watcher) { 172 fs_watcher_t **it = NULL; 173 if (!watcher || !watcher->in_active_list) return; 174 175 for (it = &active_watchers; *it; it = &(*it)->next_active) { 176 if (*it != watcher) continue; 177 *it = watcher->next_active; 178 watcher->next_active = NULL; 179 watcher->in_active_list = false; 180 return; 181 } 182} 183 184static ant_value_t fs_call_value( 185 ant_t *js, 186 ant_value_t fn, 187 ant_value_t this_val, 188 ant_value_t *args, 189 int nargs 190) { 191 ant_value_t saved_this = js->this_val; 192 ant_value_t result = js_mkundef(); 193 194 js->this_val = this_val; 195 if (vtype(fn) == T_CFUNC) result = js_as_cfunc(fn)(js, args, nargs); 196 else result = sv_vm_call(js->vm, js, fn, this_val, args, nargs, NULL, false); 197 js->this_val = saved_this; 198 199 return result; 200} 201 202static int parse_open_flags(ant_t *js, ant_value_t arg); 203static ant_value_t fs_coerce_path(ant_t *js, ant_value_t arg); 204 205static ant_value_t fs_mk_errno_error( 206 ant_t *js, int err_num, 207 const char *syscall, const char *path, const char *dest 208); 209 210static ant_value_t fs_mk_uv_error( 211 ant_t *js, int uv_code, 212 const char *syscall, const char *path, const char *dest 213); 214 215static bool fs_parse_mode(ant_t *js, ant_value_t arg, mode_t *out) { 216 if (vtype(arg) == T_NUM) { 217 double mode = js_getnum(arg); 218 if (mode < 0) return false; 219 *out = (mode_t)mode; 220 return true; 221 } 222 223 if (vtype(arg) != T_STR) return false; 224 225 size_t len = 0; 226 const char *str = js_getstr(js, arg, &len); 227 if (!str) return false; 228 229 size_t start = 0; 230 if (len > 2 && str[0] == '0' && (str[1] == 'o' || str[1] == 'O')) start = 2; 231 if (start == len) return false; 232 233 mode_t mode = 0; 234 for (size_t i = start; i < len; i++) { 235 if (str[i] < '0' || str[i] > '7') return false; 236 mode = (mode_t)((mode << 3) + (str[i] - '0')); 237 } 238 239 *out = mode; 240 return true; 241} 242 243static ant_value_t fs_stream_error(ant_t *js, ant_value_t stream_obj, const char *op, int uv_code) { 244 ant_value_t props = js_mkobj(js); 245 ant_value_t path_val = js_get(js, stream_obj, "path"); 246 const char *code = uv_err_name(uv_code); 247 248 if (code) js_set(js, props, "code", js_mkstr(js, code, strlen(code))); 249 js_set(js, props, "errno", js_mknum((double)uv_code)); 250 if (vtype(path_val) == T_STR) js_set(js, props, "path", path_val); 251 return js_mkerr_props(js, JS_ERR_TYPE, props, "%s failed: %s", op, uv_strerror(uv_code)); 252} 253 254static ant_value_t fs_stream_push_chunk(ant_t *js, ant_value_t stream_obj, ant_value_t chunk) { 255 return stream_readable_push(js, stream_obj, chunk, js_mkundef()); 256} 257 258static ant_value_t fs_stream_callback(ant_t *js, ant_value_t callback, ant_value_t value) { 259 if (!is_callable(callback)) return js_mkundef(); 260 return fs_call_value(js, callback, js_mkundef(), &value, 1); 261} 262 263static int fs_stream_close_fd_sync(ant_t *js, ant_value_t stream_obj) { 264 ant_value_t fd_val = js_get(js, stream_obj, "fd"); 265 uv_fs_t req; 266 int result = 0; 267 268 if (vtype(fd_val) != T_NUM) { 269 js_set(js, stream_obj, "pending", js_false); 270 js_set(js, stream_obj, "closed", js_true); 271 return 0; 272 } 273 274 result = uv_fs_close(uv_default_loop(), &req, (uv_file)js_getnum(fd_val), NULL); 275 uv_fs_req_cleanup(&req); 276 277 js_set(js, stream_obj, "fd", js_mknull()); 278 js_set(js, stream_obj, "pending", js_false); 279 js_set(js, stream_obj, "closed", js_true); 280 281 return result; 282} 283 284static int fs_stream_open_fd_sync(ant_t *js, ant_value_t stream_obj) { 285 ant_value_t fd_val = js_get(js, stream_obj, "fd"); 286 ant_value_t path_val = js_get(js, stream_obj, "path"); 287 ant_value_t mode_val = js_get(js, stream_obj, "mode"); 288 ant_value_t flags_val = js_get_slot(stream_obj, SLOT_FS_FLAGS); 289 290 size_t path_len = 0; 291 const char *path = NULL; 292 char *path_copy = NULL; 293 uv_fs_t req; 294 295 int flags = 0; 296 int mode = 0666; 297 int result = 0; 298 299 if (vtype(fd_val) == T_NUM) return (int)js_getnum(fd_val); 300 if (vtype(path_val) != T_STR) return UV_EINVAL; 301 302 path = js_getstr(js, path_val, &path_len); 303 if (!path) return UV_EINVAL; 304 305 path_copy = strndup(path, path_len); 306 if (!path_copy) return UV_ENOMEM; 307 308 flags = (vtype(flags_val) == T_NUM) ? (int)js_getnum(flags_val) : O_RDONLY; 309 if (vtype(mode_val) == T_NUM) mode = (int)js_getnum(mode_val); 310 311 result = uv_fs_open(uv_default_loop(), &req, path_copy, flags, mode, NULL); 312 uv_fs_req_cleanup(&req); 313 free(path_copy); 314 315 if (result < 0) return result; 316 317 js_set(js, stream_obj, "fd", js_mknum((double)result)); 318 js_set(js, stream_obj, "pending", js_false); 319 js_set(js, stream_obj, "closed", js_false); 320 321 ant_value_t open_arg = js_mknum((double)result); 322 eventemitter_emit_args(js, stream_obj, "open", &open_arg, 1); 323 eventemitter_emit_args(js, stream_obj, "ready", NULL, 0); 324 325 return result; 326} 327 328static ant_value_t fs_stream_destroy(ant_t *js, ant_value_t *args, int nargs) { 329 ant_value_t stream_obj = js_getthis(js); 330 ant_value_t err = nargs > 0 ? args[0] : js_mknull(); 331 ant_value_t callback = nargs > 1 ? args[1] : js_mkundef(); 332 int result = fs_stream_close_fd_sync(js, stream_obj); 333 334 if (result < 0 && (is_null(err) || is_undefined(err))) 335 err = fs_stream_error(js, stream_obj, "close", result); 336 if (is_undefined(err)) err = js_mknull(); 337 338 return fs_stream_callback(js, callback, err); 339} 340 341static ant_value_t fs_stream_close(ant_t *js, ant_value_t *args, int nargs) { 342 ant_value_t stream_obj = js_getthis(js); 343 344 if (nargs > 0 && is_callable(args[0])) { 345 eventemitter_add_listener(js, stream_obj, "close", args[0], true); 346 if (js_truthy(js, js_get(js, stream_obj, "closed"))) 347 fs_call_value(js, args[0], js_mkundef(), NULL, 0); 348 } 349 350 if (!js_truthy(js, js_get(js, stream_obj, "destroyed"))) { 351 ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); 352 if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, NULL, 0); 353 } 354 355 return stream_obj; 356} 357 358static ant_value_t fs_readstream__read(ant_t *js, ant_value_t *args, int nargs) { 359 ant_value_t stream_obj = js_getthis(js); 360 ant_value_t pos_val = js_get(js, stream_obj, "pos"); 361 ant_value_t end_val = js_get(js, stream_obj, "end"); 362 ant_value_t bytes_read_val = js_get(js, stream_obj, "bytesRead"); 363 364 int fd = fs_stream_open_fd_sync(js, stream_obj); 365 int64_t pos = (vtype(pos_val) == T_NUM) ? (int64_t)js_getnum(pos_val) : 0; 366 int64_t end = (vtype(end_val) == T_NUM) ? (int64_t)js_getnum(end_val) : -1; 367 368 size_t want = 16384; 369 bool reached_eof = false; 370 371 if (fd < 0) { 372 ant_value_t err = fs_stream_error(js, stream_obj, "open", fd); 373 ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); 374 if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, &err, 1); 375 return js_mkundef(); 376 } 377 378 if (nargs > 0 && vtype(args[0]) == T_NUM && js_getnum(args[0]) > 0) 379 want = (size_t)js_getnum(args[0]); 380 381 if (end >= 0) { 382 if (pos > end) { 383 if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); 384 return fs_stream_push_chunk(js, stream_obj, js_mknull()); 385 } 386 if ((int64_t)want > (end - pos + 1)) want = (size_t)(end - pos + 1); 387 } 388 389 ArrayBufferData *ab = create_array_buffer_data(want); 390 if (!ab) { 391 ant_value_t err = js_mkerr(js, "Failed to allocate ReadStream buffer"); 392 ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); 393 if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, &err, 1); 394 return js_mkundef(); 395 } 396 397 uv_fs_t req; 398 uv_buf_t buf = uv_buf_init((char *)ab->data, (unsigned int)want); 399 int result = uv_fs_read(uv_default_loop(), &req, fd, &buf, 1, pos, NULL); 400 uv_fs_req_cleanup(&req); 401 402 if (result < 0) { 403 ant_value_t err = fs_stream_error(js, stream_obj, "read", result); 404 ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); 405 free_array_buffer_data(ab); 406 if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, &err, 1); 407 return js_mkundef(); 408 } 409 410 if (result == 0) { 411 free_array_buffer_data(ab); 412 if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) 413 (void)fs_stream_close_fd_sync(js, stream_obj); 414 return fs_stream_push_chunk(js, stream_obj, js_mknull()); 415 } 416 417 ant_value_t chunk = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, (size_t)result, "Buffer"); 418 if (vtype(chunk) == T_ERR) { 419 free_array_buffer_data(ab); 420 return chunk; 421 } 422 423 if (result < (int)want) reached_eof = true; 424 if (end >= 0 && (pos + result - 1) >= end) reached_eof = true; 425 426 js_set(js, stream_obj, "pos", js_mknum((double)(pos + result))); 427 js_set(js, stream_obj, "bytesRead", js_mknum( 428 (vtype(bytes_read_val) == T_NUM 429 ? js_getnum(bytes_read_val) : 0.0) + (double)result 430 )); 431 432 fs_stream_push_chunk(js, stream_obj, chunk); 433 434 if (reached_eof) { 435 if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); 436 return fs_stream_push_chunk(js, stream_obj, js_mknull()); 437 } 438 439 return js_mkundef(); 440} 441 442static ant_value_t fs_writestream__write(ant_t *js, ant_value_t *args, int nargs) { 443 ant_value_t stream_obj = js_getthis(js); 444 ant_value_t callback = nargs > 2 ? args[2] : js_mkundef(); 445 ant_value_t pos_val = js_get(js, stream_obj, "pos"); 446 ant_value_t bytes_written_val = js_get(js, stream_obj, "bytesWritten"); 447 448 const uint8_t *bytes = NULL; 449 size_t len = 0; 450 451 int fd = fs_stream_open_fd_sync(js, stream_obj); 452 int64_t pos = (vtype(pos_val) == T_NUM) ? (int64_t)js_getnum(pos_val) : -1; 453 size_t offset = 0; 454 455 if (fd < 0) return fs_stream_callback(js, callback, fs_stream_error(js, stream_obj, "open", fd)); 456 457 if (vtype(args[0]) == T_STR) { 458 bytes = (const uint8_t *)js_getstr(js, args[0], &len); 459 } else if (!buffer_source_get_bytes(js, args[0], &bytes, &len)) { 460 return fs_stream_callback(js, callback, js_mkerr(js, "WriteStream chunk must be a string or ArrayBufferView")); 461 } 462 463 while (offset < len) { 464 uv_fs_t req; 465 uv_buf_t buf = uv_buf_init((char *)(bytes + offset), (unsigned int)(len - offset)); 466 int result = uv_fs_write(uv_default_loop(), &req, fd, &buf, 1, pos, NULL); 467 uv_fs_req_cleanup(&req); 468 469 if (result < 0) { 470 if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); 471 return fs_stream_callback(js, callback, fs_stream_error(js, stream_obj, "write", result)); 472 } 473 if (result == 0) { 474 if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); 475 return fs_stream_callback(js, callback, js_mkerr(js, "write failed: short write")); 476 } 477 478 offset += (size_t)result; 479 if (pos >= 0) pos += result; 480 } 481 482 if (pos >= 0) js_set(js, stream_obj, "pos", js_mknum((double)pos)); 483 js_set(js, stream_obj, "bytesWritten", js_mknum( 484 (vtype(bytes_written_val) == T_NUM 485 ? js_getnum(bytes_written_val) : 0.0) + (double)offset 486 )); 487 488 return fs_stream_callback(js, callback, js_mknull()); 489} 490 491static ant_value_t fs_writestream__final(ant_t *js, ant_value_t *args, int nargs) { 492 ant_value_t stream_obj = js_getthis(js); 493 ant_value_t callback = nargs > 0 ? args[0] : js_mkundef(); 494 ant_value_t value = js_mknull(); 495 496 if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) { 497 int result = fs_stream_close_fd_sync(js, stream_obj); 498 if (result < 0) value = fs_stream_error(js, stream_obj, "close", result); 499 } 500 501 return fs_stream_callback(js, callback, value); 502} 503 504static ant_value_t fs_create_readstream_impl(ant_t *js, ant_value_t path_arg, ant_value_t options_arg, ant_value_t proto) { 505 ant_value_t path_val = fs_coerce_path(js, path_arg); 506 ant_value_t options = is_object_type(options_arg) ? options_arg : js_mkobj(js); 507 ant_value_t stream_options = js_mkobj(js); 508 509 ant_value_t hwm = js_get(js, options, "highWaterMark"); 510 ant_value_t flags_raw = js_get(js, options, "flags"); 511 ant_value_t fd_val = js_get(js, options, "fd"); 512 ant_value_t start_val = js_get(js, options, "start"); 513 ant_value_t end_val = js_get(js, options, "end"); 514 ant_value_t mode_val = js_get(js, options, "mode"); 515 516 ant_value_t auto_close_val = js_get(js, options, "autoClose"); 517 ant_value_t emit_close_val = js_get(js, options, "emitClose"); 518 ant_value_t stream_obj = 0; 519 520 int flags = parse_open_flags(js, is_undefined(flags_raw) ? js_mkstr(js, "r", 1) : flags_raw); 521 if (vtype(path_val) != T_STR) return js_mkerr(js, "ReadStream path must be a string"); 522 if (vtype(hwm) == T_NUM && js_getnum(hwm) > 0) js_set(js, stream_options, "highWaterMark", hwm); 523 524 stream_obj = stream_construct_readable(js, proto, stream_options); 525 if (is_err(stream_obj)) return stream_obj; 526 527 js_set(js, stream_obj, "_read", js_mkfun(fs_readstream__read)); 528 js_set(js, stream_obj, "_destroy", js_mkfun(fs_stream_destroy)); 529 js_set(js, stream_obj, "path", path_val); 530 js_set(js, stream_obj, "flags", is_undefined(flags_raw) ? js_mkstr(js, "r", 1) : flags_raw); 531 js_set(js, stream_obj, "mode", vtype(mode_val) == T_NUM ? mode_val : js_mknum(0666)); 532 js_set(js, stream_obj, "fd", vtype(fd_val) == T_NUM ? fd_val : js_mkundef()); 533 js_set(js, stream_obj, "pending", js_bool(vtype(fd_val) != T_NUM)); 534 js_set(js, stream_obj, "closed", js_false); 535 js_set(js, stream_obj, "autoClose", is_undefined(auto_close_val) ? js_true : js_bool(js_truthy(js, auto_close_val))); 536 js_set(js, stream_obj, "emitClose", is_undefined(emit_close_val) ? js_true : js_bool(js_truthy(js, emit_close_val))); 537 js_set(js, stream_obj, "bytesRead", js_mknum(0)); 538 js_set(js, stream_obj, "start", vtype(start_val) == T_NUM ? start_val : js_mkundef()); 539 js_set(js, stream_obj, "end", vtype(end_val) == T_NUM ? end_val : js_mkundef()); 540 js_set(js, stream_obj, "pos", js_mknum(vtype(start_val) == T_NUM ? js_getnum(start_val) : 0.0)); 541 js_set_slot(stream_obj, SLOT_FS_FLAGS, js_mknum((double)flags)); 542 543 return stream_obj; 544} 545 546static ant_value_t fs_create_writestream_impl(ant_t *js, ant_value_t path_arg, ant_value_t options_arg, ant_value_t proto) { 547 ant_value_t path_val = fs_coerce_path(js, path_arg); 548 ant_value_t options = is_object_type(options_arg) ? options_arg : js_mkobj(js); 549 ant_value_t stream_options = js_mkobj(js); 550 551 ant_value_t flags_raw = js_get(js, options, "flags"); 552 ant_value_t fd_val = js_get(js, options, "fd"); 553 ant_value_t start_val = js_get(js, options, "start"); 554 ant_value_t mode_val = js_get(js, options, "mode"); 555 ant_value_t auto_close_val = js_get(js, options, "autoClose"); 556 ant_value_t emit_close_val = js_get(js, options, "emitClose"); 557 ant_value_t hwm = js_get(js, options, "highWaterMark"); 558 ant_value_t stream_obj = 0; 559 560 int flags = parse_open_flags(js, is_undefined(flags_raw) ? js_mkstr(js, "w", 1) : flags_raw); 561 double start_pos = (vtype(start_val) == T_NUM) ? js_getnum(start_val) : ((flags & O_APPEND) ? -1.0 : 0.0); 562 563 if (vtype(path_val) != T_STR) return js_mkerr(js, "WriteStream path must be a string"); 564 if (vtype(hwm) == T_NUM && js_getnum(hwm) > 0) js_set(js, stream_options, "highWaterMark", hwm); 565 566 stream_obj = stream_construct_writable(js, proto, stream_options); 567 if (is_err(stream_obj)) return stream_obj; 568 569 js_set(js, stream_obj, "_write", js_mkfun(fs_writestream__write)); 570 js_set(js, stream_obj, "_final", js_mkfun(fs_writestream__final)); 571 js_set(js, stream_obj, "_destroy", js_mkfun(fs_stream_destroy)); 572 js_set(js, stream_obj, "path", path_val); 573 js_set(js, stream_obj, "flags", is_undefined(flags_raw) ? js_mkstr(js, "w", 1) : flags_raw); 574 js_set(js, stream_obj, "mode", vtype(mode_val) == T_NUM ? mode_val : js_mknum(0666)); 575 js_set(js, stream_obj, "fd", vtype(fd_val) == T_NUM ? fd_val : js_mkundef()); 576 js_set(js, stream_obj, "pending", js_bool(vtype(fd_val) != T_NUM)); 577 js_set(js, stream_obj, "closed", js_false); 578 js_set(js, stream_obj, "autoClose", is_undefined(auto_close_val) ? js_true : js_bool(js_truthy(js, auto_close_val))); 579 js_set(js, stream_obj, "emitClose", is_undefined(emit_close_val) ? js_true : js_bool(js_truthy(js, emit_close_val))); 580 js_set(js, stream_obj, "bytesWritten", js_mknum(0)); 581 js_set(js, stream_obj, "start", vtype(start_val) == T_NUM ? start_val : js_mkundef()); 582 js_set(js, stream_obj, "pos", js_mknum(start_pos)); 583 js_set_slot(stream_obj, SLOT_FS_FLAGS, js_mknum((double)flags)); 584 585 return stream_obj; 586} 587 588static ant_value_t js_readstream_ctor(ant_t *js, ant_value_t *args, int nargs) { 589 if (nargs < 1) return js_mkerr(js, "ReadStream() requires a path argument"); 590 return fs_create_readstream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_readstream_proto); 591} 592 593static ant_value_t js_writestream_ctor(ant_t *js, ant_value_t *args, int nargs) { 594 if (nargs < 1) return js_mkerr(js, "WriteStream() requires a path argument"); 595 return fs_create_writestream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_writestream_proto); 596} 597 598static ant_value_t builtin_fs_createReadStream(ant_t *js, ant_value_t *args, int nargs) { 599 if (nargs < 1) return js_mkerr(js, "createReadStream() requires a path argument"); 600 return fs_create_readstream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_readstream_proto); 601} 602 603static ant_value_t builtin_fs_createWriteStream(ant_t *js, ant_value_t *args, int nargs) { 604 if (nargs < 1) return js_mkerr(js, "createWriteStream() requires a path argument"); 605 return fs_create_writestream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_writestream_proto); 606} 607 608static void fs_init_stream_constructors(ant_t *js) { 609 if (g_readstream_ctor && g_writestream_ctor) return; 610 611 stream_init_constructors(js); 612 613 g_readstream_proto = js_mkobj(js); 614 js_set_proto_init(g_readstream_proto, stream_readable_prototype(js)); 615 js_set(js, g_readstream_proto, "close", js_mkfun(fs_stream_close)); 616 js_set_sym(js, g_readstream_proto, get_toStringTag_sym(), js_mkstr(js, "ReadStream", 10)); 617 g_readstream_ctor = js_make_ctor(js, js_readstream_ctor, g_readstream_proto, "ReadStream", 10); 618 js_set_proto_init(g_readstream_ctor, stream_readable_constructor(js)); 619 620 g_writestream_proto = js_mkobj(js); 621 js_set_proto_init(g_writestream_proto, stream_writable_prototype(js)); 622 js_set(js, g_writestream_proto, "close", js_mkfun(fs_stream_close)); 623 js_set_sym(js, g_writestream_proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11)); 624 g_writestream_ctor = js_make_ctor(js, js_writestream_ctor, g_writestream_proto, "WriteStream", 11); 625 js_set_proto_init(g_writestream_ctor, stream_writable_constructor(js)); 626} 627 628static ant_value_t fs_make_date(ant_t *js, double ms) { 629 ant_value_t obj = js_mkobj(js); 630 ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4); 631 if (is_object_type(date_proto)) js_set_proto_init(obj, date_proto); 632 js_set_slot(obj, SLOT_DATA, tov(ms)); 633 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_DATE)); 634 return obj; 635} 636 637static ant_value_t fs_stats_object_new(ant_t *js, const fs_stat_fields_t *f) { 638 ant_value_t stat_obj = js_mkobj(js); 639 ant_value_t proto = js_get_ctor_proto(js, "Stats", 5); 640 641 if (is_object_type(proto) || is_special_object(proto)) 642 js_set_proto_init(stat_obj, proto); 643 644 js_set_slot(stat_obj, SLOT_DATA, js_mknum((double)f->mode)); 645 js_set(js, stat_obj, "size", js_mknum(f->size)); 646 js_set(js, stat_obj, "mode", js_mknum((double)f->mode)); 647 js_set(js, stat_obj, "uid", js_mknum(f->uid)); 648 js_set(js, stat_obj, "gid", js_mknum(f->gid)); 649 650 js_set(js, stat_obj, "atimeMs", js_mknum(f->atime_ms)); 651 js_set(js, stat_obj, "mtimeMs", js_mknum(f->mtime_ms)); 652 js_set(js, stat_obj, "ctimeMs", js_mknum(f->ctime_ms)); 653 js_set(js, stat_obj, "birthtimeMs", js_mknum(f->birthtime_ms)); 654 655 js_set(js, stat_obj, "atime", fs_make_date(js, f->atime_ms)); 656 js_set(js, stat_obj, "mtime", fs_make_date(js, f->mtime_ms)); 657 js_set(js, stat_obj, "ctime", fs_make_date(js, f->ctime_ms)); 658 js_set(js, stat_obj, "birthtime", fs_make_date(js, f->birthtime_ms)); 659 660 return stat_obj; 661} 662 663static double uv_ts_to_ms(uv_timespec_t ts) { 664 return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6; 665} 666 667static double posix_ts_to_ms(struct timespec ts) { 668 return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6; 669} 670 671#ifdef _WIN32 672 #define POSIX_ATIME_MS(st) ((double)(st)->st_atime * 1000.0) 673 #define POSIX_MTIME_MS(st) ((double)(st)->st_mtime * 1000.0) 674 #define POSIX_CTIME_MS(st) ((double)(st)->st_ctime * 1000.0) 675 #define POSIX_BIRTH_MS(st) 0.0 676#elif defined(__APPLE__) 677 #define POSIX_ATIME_MS(st) posix_ts_to_ms((st)->st_atimespec) 678 #define POSIX_MTIME_MS(st) posix_ts_to_ms((st)->st_mtimespec) 679 #define POSIX_CTIME_MS(st) posix_ts_to_ms((st)->st_ctimespec) 680 #define POSIX_BIRTH_MS(st) posix_ts_to_ms((st)->st_birthtimespec) 681#elif defined(__linux__) 682 #define POSIX_ATIME_MS(st) posix_ts_to_ms((st)->st_atim) 683 #define POSIX_MTIME_MS(st) posix_ts_to_ms((st)->st_mtim) 684 #define POSIX_CTIME_MS(st) posix_ts_to_ms((st)->st_ctim) 685 #define POSIX_BIRTH_MS(st) 0.0 686#else 687 #define POSIX_ATIME_MS(st) posix_ts_to_ms((st)->st_atim) 688 #define POSIX_MTIME_MS(st) posix_ts_to_ms((st)->st_mtim) 689 #define POSIX_CTIME_MS(st) posix_ts_to_ms((st)->st_ctim) 690 #define POSIX_BIRTH_MS(st) 0.0 691#endif 692 693static const fs_stat_fields_t fs_stat_fields_zero = {0}; 694 695static ant_value_t fs_stats_object_from_uv(ant_t *js, const uv_stat_t *st) { 696 if (!st) return fs_stats_object_new(js, &fs_stat_fields_zero); 697 return fs_stats_object_new(js, &(fs_stat_fields_t){ 698 .mode = (mode_t)st->st_mode, 699 .size = (double)st->st_size, 700 .uid = (double)st->st_uid, 701 .gid = (double)st->st_gid, 702 .atime_ms = uv_ts_to_ms(st->st_atim), 703 .mtime_ms = uv_ts_to_ms(st->st_mtim), 704 .ctime_ms = uv_ts_to_ms(st->st_ctim), 705 .birthtime_ms = uv_ts_to_ms(st->st_birthtim), 706 }); 707} 708 709static ant_value_t fs_stats_object_from_posix(ant_t *js, const struct stat *st) { 710 if (!st) return fs_stats_object_new(js, &fs_stat_fields_zero); 711 return fs_stats_object_new(js, &(fs_stat_fields_t){ 712 .mode = st->st_mode, 713 .size = (double)st->st_size, 714 .uid = (double)st->st_uid, 715 .gid = (double)st->st_gid, 716 .atime_ms = POSIX_ATIME_MS(st), 717 .mtime_ms = POSIX_MTIME_MS(st), 718 .ctime_ms = POSIX_CTIME_MS(st), 719 .birthtime_ms = POSIX_BIRTH_MS(st), 720 }); 721} 722 723static bool fs_stat_path_sync(const char *path, uv_stat_t *out) { 724 uv_fs_t req; 725 int rc = 0; 726 727 if (!path || !out) return false; 728 memset(out, 0, sizeof(*out)); 729 730 rc = uv_fs_stat(NULL, &req, path, NULL); 731 if (rc < 0) { 732 uv_fs_req_cleanup(&req); 733 return false; 734 } 735 736 *out = req.statbuf; 737 uv_fs_req_cleanup(&req); 738 return true; 739} 740 741static const char *fs_watch_basename(const char *path) { 742 const char *name = NULL; 743 744 if (!path || !*path) return NULL; 745 name = strrchr(path, '/'); 746#ifdef _WIN32 747 { 748 const char *alt = strrchr(path, '\\'); 749 if (!name || (alt && alt > name)) name = alt; 750 } 751#endif 752 return name ? name + 1 : path; 753} 754 755static ant_value_t fs_watch_error(ant_t *js, int status, const char *path) { 756 ant_value_t props = js_mkobj(js); 757 const char *code = uv_err_name(status); 758 js_set(js, props, "code", js_mkstr(js, code, strlen(code))); 759 760 return js_mkerr_props( 761 js, JS_ERR_TYPE, 762 props, "%s: %s", 763 path ? path : "watch", 764 uv_strerror(status) 765 ); 766} 767 768static void fs_watcher_free(fs_watcher_t *watcher) { 769 if (!watcher) return; 770 free(watcher->path); 771 free(watcher); 772} 773 774static uv_handle_t *fs_watcher_uv_handle(fs_watcher_t *watcher) { 775 if (!watcher) return NULL; 776 if (watcher->mode == FS_WATCH_MODE_STAT) return (uv_handle_t *)&watcher->handle.poll; 777 return (uv_handle_t *)&watcher->handle.event; 778} 779 780static void fs_watcher_on_handle_closed(uv_handle_t *handle) { 781 fs_watcher_t *watcher = (fs_watcher_t *)handle->data; 782 783 if (!watcher) return; 784 watcher->handle_closed = true; 785 watcher->closing = false; 786 if (watcher->finalized) fs_watcher_free(watcher); 787} 788 789static void fs_watcher_close_native(fs_watcher_t *watcher) { 790 uv_handle_t *handle = NULL; 791 792 if (!watcher) return; 793 if (watcher->closing || watcher->handle_closed) return; 794 795 handle = fs_watcher_uv_handle(watcher); 796 if (!handle) return; 797 798 fs_remove_active_watcher(watcher); 799 if (watcher->mode == FS_WATCH_MODE_STAT) uv_fs_poll_stop(&watcher->handle.poll); 800 else ant_watch_stop(&watcher->handle.event); 801 watcher->closing = true; 802 uv_close(handle, fs_watcher_on_handle_closed); 803} 804 805static void fs_watcher_emit_error(fs_watcher_t *watcher, int status) { 806 ant_value_t args[1]; 807 808 if (!watcher || vtype(watcher->obj) != T_OBJ) return; 809 args[0] = fs_watch_error(watcher->js, status, watcher->path); 810 eventemitter_emit_args(watcher->js, watcher->obj, "error", args, 1); 811} 812 813static void fs_watcher_emit_change(fs_watcher_t *watcher, const char *filename, int events) { 814 ant_value_t args[2]; 815 const char *event_name = "change"; 816 const char *name = filename; 817 818 if (!watcher || vtype(watcher->obj) != T_OBJ) return; 819 if ((events & UV_RENAME) != 0) event_name = "rename"; 820 if (!name || !*name) name = fs_watch_basename(watcher->path); 821 822 args[0] = js_mkstr(watcher->js, event_name, strlen(event_name)); 823 args[1] = name ? js_mkstr(watcher->js, name, strlen(name)) : js_mkundef(); 824 eventemitter_emit_args(watcher->js, watcher->obj, "change", args, 2); 825} 826 827static void fs_watcher_invoke_watchfile_stats( 828 fs_watcher_t *watcher, 829 const uv_stat_t *curr, 830 const uv_stat_t *prev 831) { 832 uv_stat_t curr_stat; 833 uv_stat_t prev_stat; 834 ant_value_t args[2]; 835 836 if (!watcher || !is_callable(watcher->callback)) return; 837 838 memset(&curr_stat, 0, sizeof(curr_stat)); 839 memset(&prev_stat, 0, sizeof(prev_stat)); 840 if (curr) curr_stat = *curr; 841 if (prev) prev_stat = *prev; 842 843 watcher->last_stat = curr_stat; 844 watcher->last_stat_valid = curr != NULL; 845 846 args[0] = fs_stats_object_from_uv(watcher->js, &curr_stat); 847 args[1] = fs_stats_object_from_uv(watcher->js, &prev_stat); 848 fs_call_value(watcher->js, watcher->callback, js_mkundef(), args, 2); 849} 850 851static void fs_watcher_on_event( 852 uv_fs_event_t *handle, 853 const char *filename, 854 int events, 855 int status 856) { 857 fs_watcher_t *watcher = (fs_watcher_t *)handle->data; 858 859 if (!watcher) return; 860 if (status < 0) { 861 fs_watcher_emit_error(watcher, status); 862 return; 863 } 864 865 if ((events & (UV_CHANGE | UV_RENAME)) == 0) return; 866 fs_watcher_emit_change(watcher, filename, events); 867} 868 869static void fs_watcher_on_poll( 870 uv_fs_poll_t *handle, 871 int status, 872 const uv_stat_t *prev, 873 const uv_stat_t *curr 874) { 875 fs_watcher_t *watcher = (fs_watcher_t *)handle->data; 876 uv_stat_t missing = {0}; 877 878 if (!watcher) return; 879 if (status < 0 && status != UV_ENOENT) return; 880 if (status == UV_ENOENT) { 881 if (watcher->last_stat_valid) fs_watcher_invoke_watchfile_stats(watcher, &missing, &watcher->last_stat); 882 else fs_watcher_invoke_watchfile_stats(watcher, &missing, &missing); 883 return; 884 } 885 886 fs_watcher_invoke_watchfile_stats(watcher, curr, prev); 887} 888 889static ant_value_t js_fswatcher_close(ant_t *js, ant_value_t *args, int nargs) { 890 fs_watcher_t *watcher = fs_watcher_data(js->this_val); 891 892 if (!watcher) return js->this_val; 893 fs_watcher_close_native(watcher); 894 return js->this_val; 895} 896 897static ant_value_t js_fswatcher_ref(ant_t *js, ant_value_t *args, int nargs) { 898 fs_watcher_t *watcher = fs_watcher_data(js->this_val); 899 uv_handle_t *handle = NULL; 900 901 if (!watcher || watcher->handle_closed) return js->this_val; 902 handle = fs_watcher_uv_handle(watcher); 903 if (handle) uv_ref(handle); 904 watcher->persistent = true; 905 return js->this_val; 906} 907 908static ant_value_t js_fswatcher_unref(ant_t *js, ant_value_t *args, int nargs) { 909 fs_watcher_t *watcher = fs_watcher_data(js->this_val); 910 uv_handle_t *handle = NULL; 911 912 if (!watcher || watcher->handle_closed) return js->this_val; 913 handle = fs_watcher_uv_handle(watcher); 914 if (handle) uv_unref(handle); 915 watcher->persistent = false; 916 return js->this_val; 917} 918 919static ant_value_t js_fswatcher_ctor(ant_t *js, ant_value_t *args, int nargs) { 920 return js_mkerr_typed(js, JS_ERR_TYPE, "FSWatcher cannot be constructed directly"); 921} 922 923static void fs_watcher_finalize(ant_t *js, ant_object_t *obj) { 924 fs_watcher_t *watcher = NULL; 925 if (!obj) return; 926 927 watcher = (fs_watcher_t *)obj->native.ptr; 928 obj->native.ptr = NULL; 929 if (!watcher) return; 930 931 watcher->finalized = true; 932 fs_watcher_close_native(watcher); 933 if (watcher->handle_closed) fs_watcher_free(watcher); 934} 935 936static void fs_init_watch_constructors(ant_t *js) { 937 ant_value_t events = 0; 938 ant_value_t ee_ctor = 0; 939 ant_value_t ee_proto = 0; 940 941 if (g_fswatcher_proto && g_fswatcher_ctor) return; 942 943 events = events_library(js); 944 ee_ctor = js_get(js, events, "EventEmitter"); 945 ee_proto = js_get(js, ee_ctor, "prototype"); 946 947 g_fswatcher_proto = js_mkobj(js); 948 js_set_proto_init(g_fswatcher_proto, ee_proto); 949 950 js_set(js, g_fswatcher_proto, "close", js_mkfun(js_fswatcher_close)); 951 js_set(js, g_fswatcher_proto, "ref", js_mkfun(js_fswatcher_ref)); 952 js_set(js, g_fswatcher_proto, "unref", js_mkfun(js_fswatcher_unref)); 953 954 js_set_sym(js, g_fswatcher_proto, get_toStringTag_sym(), js_mkstr(js, "FSWatcher", 9)); 955 g_fswatcher_ctor = js_make_ctor(js, js_fswatcher_ctor, g_fswatcher_proto, "FSWatcher", 9); 956} 957 958static bool fs_parse_watch_options(ant_t *js, ant_value_t *args, int nargs, fs_watch_options_t *out) { 959 ant_value_t options = js_mkundef(); 960 ant_value_t persistent_val = js_mkundef(); 961 962 if (!out) return false; 963 964 memset(out, 0, sizeof(*out)); 965 out->persistent = true; 966 out->listener = js_mkundef(); 967 968 if (nargs > 1) { 969 if (is_callable(args[1])) out->listener = args[1]; 970 else options = args[1]; 971 } 972 973 if (nargs > 2 && is_callable(args[2])) out->listener = args[2]; 974 if (vtype(options) == T_UNDEF || vtype(options) == T_NULL || vtype(options) == T_STR) return true; 975 if (vtype(options) != T_OBJ) return false; 976 977 persistent_val = js_get(js, options, "persistent"); 978 if (vtype(persistent_val) != T_UNDEF) out->persistent = js_truthy(js, persistent_val); 979 out->recursive = js_truthy(js, js_get(js, options, "recursive")); 980 return true; 981} 982 983static bool fs_parse_watchfile_options( 984 ant_t *js, 985 ant_value_t *args, 986 int nargs, 987 fs_watchfile_options_t *out 988) { 989 ant_value_t options = js_mkundef(); 990 ant_value_t persistent_val = js_mkundef(); 991 992 if (!out) return false; 993 994 memset(out, 0, sizeof(*out)); 995 out->persistent = true; 996 out->interval_ms = 5007; 997 out->listener = js_mkundef(); 998 999 if (nargs > 1) { 1000 if (is_callable(args[1])) { 1001 out->listener = args[1]; 1002 return true; 1003 } 1004 options = args[1]; 1005 } 1006 1007 if (nargs > 2 && is_callable(args[2])) out->listener = args[2]; 1008 if (!is_callable(out->listener)) return false; 1009 if (vtype(options) == T_UNDEF || vtype(options) == T_NULL) return true; 1010 if (vtype(options) != T_OBJ) return false; 1011 1012 persistent_val = js_get(js, options, "persistent"); 1013 if (vtype(persistent_val) != T_UNDEF) out->persistent = js_truthy(js, persistent_val); 1014 { 1015 ant_value_t interval_val = js_get(js, options, "interval"); 1016 if (vtype(interval_val) == T_NUM && js_getnum(interval_val) > 0) 1017 out->interval_ms = (unsigned int)js_getnum(interval_val); 1018 } 1019 return true; 1020} 1021 1022static fs_watcher_t *fs_watcher_new(ant_t *js, fs_watch_mode_t mode) { 1023 fs_watcher_t *watcher = calloc(1, sizeof(*watcher)); 1024 1025 if (!watcher) return NULL; 1026 watcher->js = js; 1027 watcher->obj = js_mkundef(); 1028 watcher->callback = js_mkundef(); 1029 watcher->persistent = true; 1030 watcher->mode = mode; 1031 1032 return watcher; 1033} 1034 1035static int fs_watcher_start_event(fs_watcher_t *watcher, const char *path, bool persistent, bool recursive) { 1036 unsigned int flags = 0; 1037 1038 if (!watcher || !path) return UV_EINVAL; 1039#ifdef UV_FS_EVENT_RECURSIVE 1040 if (recursive) flags |= UV_FS_EVENT_RECURSIVE; 1041#else 1042 (void)recursive; 1043#endif 1044 1045 watcher->persistent = persistent; 1046 return ant_watch_start( 1047 uv_default_loop(), 1048 &watcher->handle.event, 1049 path, fs_watcher_on_event, 1050 watcher, flags, &watcher->path 1051 ); 1052} 1053 1054static int fs_watcher_start_poll(fs_watcher_t *watcher, const char *path, bool persistent, unsigned int interval_ms) { 1055 int rc = 0; 1056 1057 if (!watcher || !path) return UV_EINVAL; 1058 1059 watcher->path = ant_watch_resolve_path(path); 1060 if (!watcher->path) return UV_ENOMEM; 1061 1062 rc = uv_fs_poll_init(uv_default_loop(), &watcher->handle.poll); 1063 if (rc != 0) goto fail; 1064 1065 watcher->handle.poll.data = watcher; 1066 rc = uv_fs_poll_start(&watcher->handle.poll, fs_watcher_on_poll, watcher->path, interval_ms); 1067 if (rc != 0) goto fail; 1068 1069 watcher->persistent = persistent; 1070 return 0; 1071 1072fail: 1073 free(watcher->path); 1074 watcher->path = NULL; 1075 return rc; 1076} 1077 1078static ant_value_t fs_watcher_make_object(ant_t *js, fs_watcher_t *watcher) { 1079 ant_value_t obj = 0; 1080 1081 if (!watcher) return js_mkerr(js, "Out of memory"); 1082 fs_init_watch_constructors(js); 1083 1084 obj = js_mkobj(js); 1085 js_set_proto_init(obj, g_fswatcher_proto); 1086 js_set_native_ptr(obj, watcher); 1087 js_set_native_tag(obj, FS_WATCHER_NATIVE_TAG); 1088 js_set_finalizer(obj, fs_watcher_finalize); 1089 watcher->obj = obj; 1090 1091 return obj; 1092} 1093 1094static void fs_request_fail(fs_request_t *req, int uv_code) { 1095 req->failed = 1; 1096 req->error_code = uv_code; 1097 if (req->error_msg) free(req->error_msg); 1098 req->error_msg = strdup(uv_strerror(uv_code)); 1099} 1100 1101static ant_value_t fs_coerce_file_url_path(ant_t *js, ant_value_t arg) { 1102 ant_value_t href = js_getprop_fallback(js, arg, "href"); 1103 if (vtype(href) != T_STR) return js_mkundef(); 1104 1105 const char *href_str = js_getstr(js, href, NULL); 1106 if (!href_str) return js_mkundef(); 1107 1108 url_state_t parsed = {0}; 1109 if (parse_url_to_state(href_str, NULL, &parsed) != 0) return js_mkundef(); 1110 1111 ant_value_t path = js_mkundef(); 1112 if (parsed.protocol && strcmp(parsed.protocol, "file:") == 0) { 1113 char *decoded = url_decode_component(parsed.pathname); 1114 if (decoded) { 1115 path = js_mkstr(js, decoded, strlen(decoded)); 1116 free(decoded); 1117 }} 1118 1119 url_state_clear(&parsed); 1120 return path; 1121} 1122 1123static ant_value_t fs_coerce_path(ant_t *js, ant_value_t arg) { 1124 if (vtype(arg) == T_STR) return arg; 1125 if (!is_object_type(arg)) return js_mkundef(); 1126 1127 ant_value_t path = fs_coerce_file_url_path(js, arg); 1128 if (!is_undefined(path)) return path; 1129 1130 path = js_get(js, arg, "pathname"); 1131 if (vtype(path) == T_STR) return path; 1132 1133 return js_mkundef(); 1134} 1135 1136static int fs_remove_path_recursive(const char *path, bool recursive, bool force) { 1137 uv_fs_t req; 1138 1139 int result = uv_fs_lstat(NULL, &req, path, NULL); 1140 if (result < 0) { 1141 uv_fs_req_cleanup(&req); 1142 return (force && result == UV_ENOENT) ? 0 : result; 1143 } 1144 1145 uv_stat_t statbuf = req.statbuf; 1146 uv_fs_req_cleanup(&req); 1147 1148 if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { 1149 result = uv_fs_unlink(NULL, &req, path, NULL); 1150 uv_fs_req_cleanup(&req); 1151 return (force && result == UV_ENOENT) ? 0 : result; 1152 } 1153 1154 if (!recursive) return UV_EISDIR; 1155 result = uv_fs_scandir(NULL, &req, path, 0, NULL); 1156 1157 if (result < 0) { 1158 uv_fs_req_cleanup(&req); 1159 return (force && result == UV_ENOENT) ? 0 : result; 1160 } 1161 1162 for (;;) { 1163 uv_dirent_t entry; 1164 result = uv_fs_scandir_next(&req, &entry); 1165 if (result == UV_EOF) break; 1166 if (result < 0) { 1167 uv_fs_req_cleanup(&req); 1168 return result; 1169 } 1170 1171 size_t path_len = strlen(path); 1172 size_t name_len = strlen(entry.name); 1173 1174 bool needs_sep = path_len > 0 && path[path_len - 1] != '/'; 1175 char *child = malloc(path_len + (needs_sep ? 1u : 0u) + name_len + 1u); 1176 if (!child) { 1177 uv_fs_req_cleanup(&req); 1178 return UV_ENOMEM; 1179 } 1180 1181 memcpy(child, path, path_len); 1182 if (needs_sep) child[path_len++] = '/'; 1183 memcpy(child + path_len, entry.name, name_len + 1u); 1184 1185 result = fs_remove_path_recursive(child, true, force); 1186 free(child); 1187 if (result < 0) { 1188 uv_fs_req_cleanup(&req); 1189 return result; 1190 } 1191 } 1192 1193 uv_fs_req_cleanup(&req); 1194 result = uv_fs_rmdir(NULL, &req, path, NULL); 1195 uv_fs_req_cleanup(&req); 1196 return (force && result == UV_ENOENT) ? 0 : result; 1197} 1198 1199static bool fs_parse_rm_options(ant_t *js, ant_value_t options, bool *recursive_out, bool *force_out) { 1200 if (recursive_out) *recursive_out = false; 1201 if (force_out) *force_out = false; 1202 1203 if (vtype(options) == T_UNDEF || vtype(options) == T_NULL) return true; 1204 if (vtype(options) != T_OBJ) return false; 1205 1206 if (recursive_out) *recursive_out = js_truthy(js, js_get(js, options, "recursive")); 1207 if (force_out) *force_out = js_truthy(js, js_get(js, options, "force")); 1208 return true; 1209} 1210 1211static ant_value_t fs_rm_impl(ant_t *js, ant_value_t *args, int nargs, bool return_promise) { 1212 ant_value_t promise = 0; 1213 ant_value_t path_val; 1214 size_t path_len = 0; 1215 const char *path = NULL; 1216 bool recursive = false; 1217 bool force = false; 1218 int result; 1219 1220 if (return_promise) promise = js_mkpromise(js); 1221 if (nargs < 1) { 1222 ant_value_t err = js_mkerr(js, "rm() requires a path argument"); 1223 if (!return_promise) return err; 1224 js_reject_promise(js, promise, err); 1225 return promise; 1226 } 1227 1228 path_val = fs_coerce_path(js, args[0]); 1229 if (vtype(path_val) != T_STR) { 1230 ant_value_t err = js_mkerr(js, "rm() path must be a string"); 1231 if (!return_promise) return err; 1232 js_reject_promise(js, promise, err); 1233 return promise; 1234 } 1235 1236 path = js_getstr(js, path_val, &path_len); 1237 if (!path) { 1238 ant_value_t err = js_mkerr(js, "Failed to get path string"); 1239 if (!return_promise) return err; 1240 js_reject_promise(js, promise, err); 1241 return promise; 1242 } 1243 1244 if (!fs_parse_rm_options(js, nargs >= 2 ? args[1] : js_mkundef(), &recursive, &force)) { 1245 ant_value_t err = js_mkerr_typed(js, JS_ERR_TYPE, "rm() options must be an object"); 1246 if (!return_promise) return err; 1247 js_reject_promise(js, promise, err); 1248 return promise; 1249 } 1250 1251 result = fs_remove_path_recursive(path, recursive, force); 1252 if (result < 0) { 1253 ant_value_t err = js_mkerr(js, "%s", uv_strerror(result)); 1254 if (!return_promise) return err; 1255 js_reject_promise(js, promise, err); 1256 return promise; 1257 } 1258 1259 if (!return_promise) return js_mkundef(); 1260 js_resolve_promise(js, promise, js_mkundef()); 1261 return promise; 1262} 1263 1264static fs_encoding_t parse_encoding(ant_t *js, ant_value_t arg) { 1265 size_t len; 1266 const char *str = NULL; 1267 1268 if (vtype(arg) == T_STR) str = js_getstr(js, arg, &len); 1269 else if (vtype(arg) == T_OBJ) { 1270 ant_value_t enc = js_get(js, arg, "encoding"); 1271 if (vtype(enc) == T_STR) str = js_getstr(js, enc, &len); 1272 } 1273 1274 if (!str) return FS_ENC_NONE; 1275 1276 if (len == 4 && memcmp(str, "utf8", 4) == 0) return FS_ENC_UTF8; 1277 if (len == 5 && memcmp(str, "utf-8", 5) == 0) return FS_ENC_UTF8; 1278 if (len == 7 && memcmp(str, "utf16le", 7) == 0) return FS_ENC_UTF16LE; 1279 if (len == 4 && memcmp(str, "ucs2", 4) == 0) return FS_ENC_UTF16LE; 1280 if (len == 5 && memcmp(str, "ucs-2", 5) == 0) return FS_ENC_UTF16LE; 1281 if (len == 6 && memcmp(str, "latin1", 6) == 0) return FS_ENC_LATIN1; 1282 if (len == 6 && memcmp(str, "binary", 6) == 0) return FS_ENC_LATIN1; 1283 if (len == 6 && memcmp(str, "base64", 6) == 0) return FS_ENC_BASE64; 1284 if (len == 9 && memcmp(str, "base64url", 9) == 0) return FS_ENC_BASE64URL; 1285 if (len == 3 && memcmp(str, "hex", 3) == 0) return FS_ENC_HEX; 1286 if (len == 5 && memcmp(str, "ascii", 5) == 0) return FS_ENC_ASCII; 1287 1288 return FS_ENC_NONE; 1289} 1290 1291static ant_value_t encode_data(ant_t *js, const char *data, size_t len, fs_encoding_t enc) { 1292 switch (enc) { 1293 case FS_ENC_UTF8: 1294 case FS_ENC_LATIN1: 1295 case FS_ENC_ASCII: return js_mkstr(js, data, len); 1296 1297 case FS_ENC_HEX: { 1298 char *out = malloc(len * 2); 1299 if (!out) return js_mkerr(js, "Out of memory"); 1300 for (size_t i = 0; i < len; i++) { 1301 out[i * 2] = hex_char((unsigned char)data[i] >> 4); 1302 out[i * 2 + 1] = hex_char(data[i]); 1303 } 1304 ant_value_t result = js_mkstr(js, out, len * 2); 1305 free(out); return result; 1306 } 1307 1308 case FS_ENC_BASE64: { 1309 size_t out_len; 1310 char *out = ant_base64_encode((const uint8_t *)data, len, &out_len); 1311 if (!out) return js_mkerr(js, "Out of memory"); 1312 ant_value_t result = js_mkstr(js, out, out_len); 1313 free(out); return result; 1314 } 1315 1316 case FS_ENC_BASE64URL: { 1317 size_t out_len; 1318 char *out = ant_base64_encode((const uint8_t *)data, len, &out_len); 1319 if (!out) return js_mkerr(js, "Out of memory"); 1320 for (size_t i = 0; i < out_len; i++) { 1321 if (out[i] == '+') out[i] = '-'; 1322 else if (out[i] == '/') out[i] = '_'; 1323 } 1324 while (out_len > 0 && out[out_len - 1] == '=') out_len--; 1325 ant_value_t result = js_mkstr(js, out, out_len); 1326 free(out); return result; 1327 } 1328 1329 case FS_ENC_UTF16LE: { 1330 const unsigned char *s = (const unsigned char *)data; 1331 char *out = malloc((len / 2) * 4); 1332 if (!out) return js_mkerr(js, "Out of memory"); 1333 size_t j = 0; 1334 for (size_t i = 0; i + 1 < len; i += 2) { 1335 uint32_t cp = s[i] | (s[i + 1] << 8); 1336 bool is_hi = cp >= 0xD800 && cp <= 0xDBFF && i + 3 < len; 1337 uint32_t lo = is_hi ? s[i + 2] | (s[i + 3] << 8) : 0; 1338 bool is_pair = is_hi && lo >= 0xDC00 && lo <= 0xDFFF; 1339 if (is_pair) { cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00); i += 2; } 1340 j += utf8_encode(cp, out + j); 1341 } 1342 ant_value_t result = js_mkstr(js, out, j); 1343 free(out); return result; 1344 } 1345 1346 default: return js_mkstr(js, data, len); 1347 } 1348} 1349 1350static void free_fs_request(fs_request_t *req) { 1351 if (!req) return; 1352 1353 if (req->path) free(req->path); 1354 if (req->path2) free(req->path2); 1355 if (req->data) free(req->data); 1356 if (req->error_msg) free(req->error_msg); 1357 1358 uv_fs_req_cleanup(&req->uv_req); 1359 free(req); 1360} 1361 1362static ant_value_t fs_rejected_promise(ant_t *js, ant_value_t err) { 1363 ant_value_t promise = js_mkpromise(js); 1364 js_reject_promise(js, promise, err); 1365 return promise; 1366} 1367 1368static ant_value_t fs_resolved_promise(ant_t *js, ant_value_t value) { 1369 ant_value_t promise = js_mkpromise(js); 1370 js_resolve_promise(js, promise, value); 1371 return promise; 1372} 1373 1374static ant_value_t fs_filehandle_require_this(ant_t *js) { 1375 ant_value_t this_obj = js_getthis(js); 1376 if (!is_object_type(this_obj) || !js_check_native_tag(this_obj, FS_FILEHANDLE_NATIVE_TAG)) 1377 return js_mkerr_typed(js, JS_ERR_TYPE, "FileHandle method called on incompatible receiver"); 1378 return this_obj; 1379} 1380 1381static ant_value_t fs_filehandle_fd_value(ant_t *js, ant_value_t handle_obj) { 1382 ant_value_t fd_val = js_get_slot(handle_obj, SLOT_DATA); 1383 if (vtype(fd_val) != T_NUM) fd_val = js_get(js, handle_obj, "fd"); 1384 return fd_val; 1385} 1386 1387static ant_value_t fs_filehandle_get_fd(ant_t *js, ant_value_t handle_obj) { 1388 ant_value_t fd_val = fs_filehandle_fd_value(js, handle_obj); 1389 if (vtype(fd_val) != T_NUM || js_getnum(fd_val) < 0) 1390 return js_mkerr_typed(js, JS_ERR_TYPE, "FileHandle is closed"); 1391 return fd_val; 1392} 1393 1394static void fs_filehandle_mark_closed(ant_t *js, ant_value_t handle_obj) { 1395 js_set_slot(handle_obj, SLOT_DATA, js_mkundef()); 1396 js_set(js, handle_obj, "fd", js_mknum(-1)); 1397} 1398 1399static ant_value_t builtin_fs_filehandle_close(ant_t *js, ant_value_t *args, int nargs) { 1400 ant_value_t handle_obj = fs_filehandle_require_this(js); 1401 if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj); 1402 1403 ant_value_t fd_val = fs_filehandle_fd_value(js, handle_obj); 1404 if (vtype(fd_val) != T_NUM || js_getnum(fd_val) < 0) 1405 return fs_resolved_promise(js, js_mkundef()); 1406 1407 uv_fs_t req; 1408 int result = uv_fs_close(uv_default_loop(), &req, (uv_file)(int)js_getnum(fd_val), NULL); 1409 uv_fs_req_cleanup(&req); 1410 if (result < 0) return fs_rejected_promise(js, fs_mk_uv_error(js, result, "close", NULL, NULL)); 1411 1412 fs_filehandle_mark_closed(js, handle_obj); 1413 return fs_resolved_promise(js, js_mkundef()); 1414} 1415 1416static ant_value_t builtin_fs_filehandle_stat(ant_t *js, ant_value_t *args, int nargs) { 1417 ant_value_t handle_obj = fs_filehandle_require_this(js); 1418 if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj); 1419 1420 ant_value_t fd_val = fs_filehandle_get_fd(js, handle_obj); 1421 if (is_err(fd_val)) return fs_rejected_promise(js, fd_val); 1422 1423 uv_fs_t req; 1424 int result = uv_fs_fstat(NULL, &req, (uv_file)(int)js_getnum(fd_val), NULL); 1425 if (result < 0) { 1426 ant_value_t err = fs_mk_uv_error(js, result, "fstat", NULL, NULL); 1427 uv_fs_req_cleanup(&req); 1428 return fs_rejected_promise(js, err); 1429 } 1430 1431 ant_value_t stat_obj = fs_stats_object_from_uv(js, &req.statbuf); 1432 uv_fs_req_cleanup(&req); 1433 return fs_resolved_promise(js, stat_obj); 1434} 1435 1436static ant_value_t builtin_fs_filehandle_sync(ant_t *js, ant_value_t *args, int nargs) { 1437 ant_value_t handle_obj = fs_filehandle_require_this(js); 1438 if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj); 1439 1440 ant_value_t fd_val = fs_filehandle_get_fd(js, handle_obj); 1441 if (is_err(fd_val)) return fs_rejected_promise(js, fd_val); 1442 1443 uv_fs_t req; 1444 int result = uv_fs_fsync(uv_default_loop(), &req, (uv_file)(int)js_getnum(fd_val), NULL); 1445 uv_fs_req_cleanup(&req); 1446 if (result < 0) return fs_rejected_promise(js, fs_mk_uv_error(js, result, "fsync", NULL, NULL)); 1447 1448 return fs_resolved_promise(js, js_mkundef()); 1449} 1450 1451static ant_value_t builtin_fs_filehandle_read(ant_t *js, ant_value_t *args, int nargs) { 1452 ant_value_t handle_obj = fs_filehandle_require_this(js); 1453 if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj); 1454 1455 ant_value_t fd_val = fs_filehandle_get_fd(js, handle_obj); 1456 if (is_err(fd_val)) return fs_rejected_promise(js, fd_val); 1457 if (nargs < 1) return fs_rejected_promise(js, js_mkerr(js, "FileHandle.read() requires a buffer argument")); 1458 1459 TypedArrayData *ta_data = buffer_get_typedarray_data(args[0]); 1460 if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) 1461 return fs_rejected_promise(js, js_mkerr(js, "FileHandle.read() buffer must be a Buffer or TypedArray")); 1462 1463 size_t buf_len = ta_data->byte_length; 1464 size_t offset = 0; 1465 size_t length = buf_len; 1466 int64_t position = -1; 1467 1468 if (nargs >= 2 && vtype(args[1]) == T_NUM) offset = (size_t)js_getnum(args[1]); 1469 if (nargs >= 3 && vtype(args[2]) == T_NUM) length = (size_t)js_getnum(args[2]); 1470 if (nargs >= 4 && vtype(args[3]) == T_NUM) position = (int64_t)js_getnum(args[3]); 1471 1472 if (offset > buf_len) return fs_rejected_promise(js, js_mkerr(js, "offset is out of bounds")); 1473 if (offset + length > buf_len) return fs_rejected_promise(js, js_mkerr(js, "length extends beyond buffer")); 1474 1475 uint8_t *buf_data = ta_data->buffer->data + ta_data->byte_offset; 1476 uv_buf_t buf = uv_buf_init((char *)(buf_data + offset), (unsigned int)length); 1477 1478 uv_fs_t req; 1479 int result = uv_fs_read(uv_default_loop(), &req, (uv_file)(int)js_getnum(fd_val), &buf, 1, position, NULL); 1480 uv_fs_req_cleanup(&req); 1481 if (result < 0) return fs_rejected_promise(js, fs_mk_uv_error(js, result, "read", NULL, NULL)); 1482 1483 ant_value_t out = js_mkobj(js); 1484 js_set(js, out, "bytesRead", js_mknum((double)result)); 1485 js_set(js, out, "buffer", args[0]); 1486 1487 return fs_resolved_promise(js, out); 1488} 1489 1490static ant_value_t builtin_fs_filehandle_write(ant_t *js, ant_value_t *args, int nargs) { 1491 ant_value_t handle_obj = fs_filehandle_require_this(js); 1492 if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj); 1493 1494 ant_value_t fd_val = fs_filehandle_get_fd(js, handle_obj); 1495 if (is_err(fd_val)) return fs_rejected_promise(js, fd_val); 1496 if (nargs < 1) return fs_rejected_promise(js, js_mkerr(js, "FileHandle.write() requires a data argument")); 1497 1498 const char *write_data = NULL; 1499 size_t write_len = 0; 1500 int64_t position = -1; 1501 1502 if (vtype(args[0]) == T_STR) { 1503 write_data = js_getstr(js, args[0], &write_len); 1504 if (!write_data) return fs_rejected_promise(js, js_mkerr(js, "Failed to get string")); 1505 if (nargs >= 2 && vtype(args[1]) == T_NUM) position = (int64_t)js_getnum(args[1]); 1506 } else { 1507 TypedArrayData *ta_data = buffer_get_typedarray_data(args[0]); 1508 if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) 1509 return fs_rejected_promise(js, js_mkerr(js, "FileHandle.write() data must be a Buffer, TypedArray, DataView, or string")); 1510 1511 uint8_t *buf_data = ta_data->buffer->data + ta_data->byte_offset; 1512 size_t buf_len = ta_data->byte_length; 1513 size_t offset = 0; 1514 size_t length = buf_len; 1515 1516 if (nargs >= 2) { 1517 if (vtype(args[1]) == T_OBJ) { 1518 ant_value_t off_val = js_get(js, args[1], "offset"); 1519 ant_value_t len_val = js_get(js, args[1], "length"); 1520 ant_value_t pos_val = js_get(js, args[1], "position"); 1521 if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); 1522 if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); 1523 else length = buf_len - offset; 1524 if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); 1525 } else if (vtype(args[1]) == T_NUM) { 1526 offset = (size_t)js_getnum(args[1]); 1527 length = buf_len - offset; 1528 if (nargs >= 3 && vtype(args[2]) == T_NUM) length = (size_t)js_getnum(args[2]); 1529 if (nargs >= 4 && vtype(args[3]) == T_NUM) position = (int64_t)js_getnum(args[3]); 1530 }} 1531 1532 if (offset > buf_len) return fs_rejected_promise(js, js_mkerr(js, "offset is out of bounds")); 1533 if (offset + length > buf_len) return fs_rejected_promise(js, js_mkerr(js, "length extends beyond buffer")); 1534 write_data = (const char *)(buf_data + offset); 1535 write_len = length; 1536 } 1537 1538 uv_buf_t buf = uv_buf_init((char *)write_data, (unsigned int)write_len); 1539 uv_fs_t req; 1540 int result = uv_fs_write(uv_default_loop(), &req, (uv_file)(int)js_getnum(fd_val), &buf, 1, position, NULL); 1541 uv_fs_req_cleanup(&req); 1542 if (result < 0) return fs_rejected_promise(js, fs_mk_uv_error(js, result, "write", NULL, NULL)); 1543 1544 ant_value_t out = js_mkobj(js); 1545 js_set(js, out, "bytesWritten", js_mknum((double)result)); 1546 js_set(js, out, "buffer", args[0]); 1547 1548 return fs_resolved_promise(js, out); 1549} 1550 1551static ant_value_t builtin_fs_filehandle_writeFile(ant_t *js, ant_value_t *args, int nargs) { 1552 ant_value_t handle_obj = fs_filehandle_require_this(js); 1553 if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj); 1554 1555 ant_value_t fd_val = fs_filehandle_get_fd(js, handle_obj); 1556 if (is_err(fd_val)) return fs_rejected_promise(js, fd_val); 1557 if (nargs < 1) return fs_rejected_promise(js, js_mkerr(js, "FileHandle.writeFile() requires data")); 1558 1559 const char *data = NULL; 1560 size_t len = 0; 1561 1562 if (vtype(args[0]) == T_STR) { 1563 data = js_getstr(js, args[0], &len); 1564 if (!data) return fs_rejected_promise(js, js_mkerr(js, "Failed to get string")); 1565 } else { 1566 TypedArrayData *ta_data = buffer_get_typedarray_data(args[0]); 1567 if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) 1568 return fs_rejected_promise(js, js_mkerr(js, "FileHandle.writeFile() data must be a Buffer, TypedArray, DataView, or string")); 1569 data = (const char *)(ta_data->buffer->data + ta_data->byte_offset); 1570 len = ta_data->byte_length; 1571 } 1572 1573 size_t written = 0; 1574 while (written < len) { 1575 uv_buf_t buf = uv_buf_init((char *)(data + written), (unsigned int)(len - written)); 1576 uv_fs_t req; 1577 int result = uv_fs_write(uv_default_loop(), &req, (uv_file)(int)js_getnum(fd_val), &buf, 1, -1, NULL); 1578 uv_fs_req_cleanup(&req); 1579 if (result < 0) return fs_rejected_promise(js, fs_mk_uv_error(js, result, "write", NULL, NULL)); 1580 if (result == 0) return fs_rejected_promise(js, js_mkerr(js, "short write")); 1581 written += (size_t)result; 1582 } 1583 1584 return fs_resolved_promise(js, js_mkundef()); 1585} 1586 1587static void fs_init_filehandle_proto(ant_t *js) { 1588 if (is_object_type(g_filehandle_proto)) return; 1589 g_filehandle_proto = js_mkobj(js); 1590 js_set_native_tag(g_filehandle_proto, FS_FILEHANDLE_NATIVE_TAG); 1591 js_set(js, g_filehandle_proto, "close", js_mkfun(builtin_fs_filehandle_close)); 1592 js_set(js, g_filehandle_proto, "stat", js_mkfun(builtin_fs_filehandle_stat)); 1593 js_set(js, g_filehandle_proto, "sync", js_mkfun(builtin_fs_filehandle_sync)); 1594 js_set(js, g_filehandle_proto, "read", js_mkfun(builtin_fs_filehandle_read)); 1595 js_set(js, g_filehandle_proto, "write", js_mkfun(builtin_fs_filehandle_write)); 1596 js_set(js, g_filehandle_proto, "writeFile", js_mkfun(builtin_fs_filehandle_writeFile)); 1597 js_set_sym(js, g_filehandle_proto, get_toStringTag_sym(), js_mkstr(js, "FileHandle", 10)); 1598} 1599 1600static ant_value_t fs_make_filehandle(ant_t *js, int fd) { 1601 fs_init_filehandle_proto(js); 1602 ant_value_t handle = js_mkobj(js); 1603 1604 js_set_native_tag(handle, FS_FILEHANDLE_NATIVE_TAG); 1605 js_set_proto_init(handle, g_filehandle_proto); 1606 js_set_slot(handle, SLOT_DATA, js_mknum((double)fd)); 1607 js_set(js, handle, "fd", js_mknum((double)fd)); 1608 1609 return handle; 1610} 1611 1612static void remove_pending_request(fs_request_t *req) { 1613 if (!req || !pending_requests) return; 1614 unsigned int len = utarray_len(pending_requests); 1615 1616 for (unsigned int i = 0; i < len; i++) { 1617 if (*(fs_request_t**)utarray_eltptr(pending_requests, i) != req) continue; 1618 utarray_erase(pending_requests, i, 1); break; 1619 } 1620} 1621 1622static ant_value_t fs_read_to_uint8array(ant_t *js, const char *data, size_t len) { 1623 ArrayBufferData *ab = create_array_buffer_data(len); 1624 if (!ab) return js_mkundef(); 1625 memcpy(ab->data, data, len); 1626 ant_value_t result = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Buffer"); 1627 if (vtype(result) == T_ERR) free_array_buffer_data(ab); 1628 return result; 1629} 1630 1631static void complete_request(fs_request_t *req) { 1632 if (req->failed) { 1633 const char *err_msg = req->error_msg 1634 ? req->error_msg 1635 : "Unknown error"; 1636 1637 ant_value_t props = js_mkobj(req->js); 1638 ant_value_t reject_value; 1639 1640 if (req->error_code) { 1641 const char *code = uv_err_name(req->error_code); 1642 js_set(req->js, props, "code", js_mkstr(req->js, code, strlen(code))); 1643 js_set(req->js, props, "errno", js_mknum((double)req->error_code)); 1644 if (req->path) js_set(req->js, props, "path", js_mkstr(req->js, req->path, strlen(req->path))); 1645 if (req->path2) js_set(req->js, props, "dest", js_mkstr(req->js, req->path2, strlen(req->path2))); 1646 reject_value = js_mkerr_props(req->js, JS_ERR_TYPE, props, "%s", err_msg); 1647 } else reject_value = js_mkerr(req->js, "%s", err_msg); 1648 1649 if (is_err(reject_value)) { 1650 reject_value = req->js->thrown_exists ? req->js->thrown_value : js_mkundef(); 1651 if (!req->js->thrown_exists) 1652 reject_value = js_mkstr(req->js, err_msg, strlen(err_msg)); 1653 req->js->thrown_exists = false; 1654 req->js->thrown_value = js_mkundef(); 1655 } 1656 js_reject_promise(req->js, req->promise, reject_value); 1657 } else { 1658 ant_value_t result = js_mkundef(); 1659 if (req->op_type == FS_OP_READ && req->data) { 1660 if (req->encoding != FS_ENC_NONE) 1661 result = encode_data(req->js, req->data, req->data_len, req->encoding); 1662 else result = fs_read_to_uint8array(req->js, req->data, req->data_len); 1663 } else if (req->op_type == FS_OP_READ_BYTES && req->data) 1664 result = js_mkstr(req->js, req->data, req->data_len); 1665 else if (req->op_type == FS_OP_REALPATH && req->data) 1666 result = js_mkstr(req->js, req->data, req->data_len); 1667 else if (req->op_type == FS_OP_MKDTEMP && req->data) 1668 result = js_mkstr(req->js, req->data, req->data_len); 1669 js_resolve_promise(req->js, req->promise, result); 1670 } 1671 1672 remove_pending_request(req); 1673 free_fs_request(req); 1674} 1675 1676static void on_read_complete(uv_fs_t *uv_req) { 1677 fs_request_t *req = (fs_request_t *)uv_req->data; 1678 1679 if (uv_req->result < 0) { 1680 fs_request_fail(req, (int)uv_req->result); 1681 req->completed = 1; 1682 complete_request(req); 1683 return; 1684 } 1685 1686 req->data_len = uv_req->result; 1687 uv_fs_t close_req; 1688 1689 uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); 1690 uv_fs_req_cleanup(&close_req); 1691 1692 req->completed = 1; 1693 complete_request(req); 1694} 1695 1696static void on_open_for_read(uv_fs_t *uv_req) { 1697 fs_request_t *req = (fs_request_t *)uv_req->data; 1698 1699 if (uv_req->result < 0) { 1700 fs_request_fail(req, (int)uv_req->result); 1701 req->completed = 1; 1702 complete_request(req); 1703 return; 1704 } 1705 1706 req->fd = (int)uv_req->result; 1707 uv_fs_req_cleanup(uv_req); 1708 1709 uv_fs_t stat_req; 1710 int stat_result = uv_fs_fstat(uv_default_loop(), &stat_req, req->fd, NULL); 1711 1712 if (stat_result < 0) { 1713 fs_request_fail(req, stat_result); 1714 req->completed = 1; 1715 uv_fs_t close_req; 1716 uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); 1717 uv_fs_req_cleanup(&close_req); 1718 complete_request(req); 1719 return; 1720 } 1721 1722 size_t file_size = stat_req.statbuf.st_size; 1723 uv_fs_req_cleanup(&stat_req); 1724 1725 size_t alloc_size = (req->op_type == FS_OP_READ) ? file_size + 1 : file_size; 1726 req->data = malloc(alloc_size); 1727 if (!req->data) { 1728 req->failed = 1; 1729 req->error_msg = strdup("Out of memory"); 1730 req->completed = 1; 1731 uv_fs_t close_req; 1732 uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); 1733 uv_fs_req_cleanup(&close_req); 1734 complete_request(req); 1735 return; 1736 } 1737 1738 uv_buf_t buf = uv_buf_init(req->data, (unsigned int)file_size); 1739 int read_result = uv_fs_read(uv_default_loop(), uv_req, req->fd, &buf, 1, 0, on_read_complete); 1740 1741 if (read_result < 0) { 1742 fs_request_fail(req, read_result); 1743 req->completed = 1; 1744 uv_fs_t close_req; 1745 uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); 1746 uv_fs_req_cleanup(&close_req); 1747 complete_request(req); 1748 return; 1749 } 1750} 1751 1752static void on_write_complete(uv_fs_t *uv_req) { 1753 fs_request_t *req = (fs_request_t *)uv_req->data; 1754 1755 if (uv_req->result < 0) { 1756 fs_request_fail(req, (int)uv_req->result); 1757 } 1758 1759 uv_fs_t close_req; 1760 uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); 1761 uv_fs_req_cleanup(&close_req); 1762 1763 req->completed = 1; 1764 complete_request(req); 1765} 1766 1767static void on_open_for_write(uv_fs_t *uv_req) { 1768 fs_request_t *req = (fs_request_t *)uv_req->data; 1769 1770 if (uv_req->result < 0) { 1771 fs_request_fail(req, (int)uv_req->result); 1772 req->completed = 1; 1773 complete_request(req); 1774 return; 1775 } 1776 1777 req->fd = (int)uv_req->result; 1778 uv_fs_req_cleanup(uv_req); 1779 1780 uv_buf_t buf = uv_buf_init(req->data, (unsigned int)req->data_len); 1781 int write_result = uv_fs_write(uv_default_loop(), uv_req, req->fd, &buf, 1, 0, on_write_complete); 1782 1783 if (write_result < 0) { 1784 fs_request_fail(req, write_result); 1785 req->completed = 1; 1786 uv_fs_t close_req; 1787 uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); 1788 uv_fs_req_cleanup(&close_req); 1789 complete_request(req); 1790 return; 1791 } 1792} 1793 1794static void on_unlink_complete(uv_fs_t *uv_req) { 1795 fs_request_t *req = (fs_request_t *)uv_req->data; 1796 1797 if (uv_req->result < 0) { 1798 fs_request_fail(req, (int)uv_req->result); 1799 } 1800 1801 uv_fs_req_cleanup(uv_req); 1802 req->completed = 1; 1803 complete_request(req); 1804} 1805 1806static void on_rename_complete(uv_fs_t *uv_req) { 1807 fs_request_t *req = (fs_request_t *)uv_req->data; 1808 1809 if (uv_req->result < 0) { 1810 fs_request_fail(req, (int)uv_req->result); 1811 } 1812 1813 uv_fs_req_cleanup(uv_req); 1814 req->completed = 1; 1815 complete_request(req); 1816} 1817 1818static int mkdirp(const char *path, mode_t mode) { 1819 int result = -1; 1820 char *tmp = strdup(path); 1821 if (!tmp) goto done; 1822 1823 size_t len = strlen(tmp); 1824 if (len > 0 && tmp[len - 1] == '/') tmp[len - 1] = '\0'; 1825 1826 for (char *p = tmp + 1; *p; p++) { 1827 if (*p != '/') continue; 1828 *p = '\0'; 1829#ifdef _WIN32 1830 _mkdir(tmp); 1831#else 1832 mkdir(tmp, mode); 1833#endif 1834 *p = '/'; 1835 } 1836 1837#ifdef _WIN32 1838 result = _mkdir(tmp); 1839#else 1840 result = mkdir(tmp, mode); 1841#endif 1842 if (result != 0 && errno == EEXIST) result = 0; 1843 1844 free(tmp); 1845done: 1846 return result; 1847} 1848 1849static void on_mkdir_complete(uv_fs_t *uv_req) { 1850 fs_request_t *req = (fs_request_t *)uv_req->data; 1851 1852 if (uv_req->result < 0) { 1853 if (!req->recursive || uv_req->result != UV_EEXIST) { 1854 fs_request_fail(req, (int)uv_req->result); 1855 }} 1856 1857 uv_fs_req_cleanup(uv_req); 1858 req->completed = 1; 1859 complete_request(req); 1860} 1861 1862static void on_rmdir_complete(uv_fs_t *uv_req) { 1863 fs_request_t *req = (fs_request_t *)uv_req->data; 1864 1865 if (uv_req->result < 0) { 1866 fs_request_fail(req, (int)uv_req->result); 1867 } 1868 1869 uv_fs_req_cleanup(uv_req); 1870 req->completed = 1; 1871 complete_request(req); 1872} 1873 1874static void on_stat_complete(uv_fs_t *uv_req) { 1875 fs_request_t *req = (fs_request_t *)uv_req->data; 1876 1877 if (uv_req->result < 0) { 1878 fs_request_fail(req, (int)uv_req->result); 1879 req->completed = 1; 1880 complete_request(req); 1881 return; 1882 } 1883 1884 ant_value_t stat_obj = fs_stats_object_from_uv(req->js, &uv_req->statbuf); 1885 1886 req->completed = 1; 1887 js_resolve_promise(req->js, req->promise, stat_obj); 1888 remove_pending_request(req); 1889 free_fs_request(req); 1890} 1891 1892static void on_realpath_complete(uv_fs_t *uv_req) { 1893 fs_request_t *req = (fs_request_t *)uv_req->data; 1894 1895 if (uv_req->result < 0 || !uv_req->ptr) { 1896 fs_request_fail(req, (int)uv_req->result); 1897 req->completed = 1; 1898 complete_request(req); 1899 return; 1900 } 1901 1902 req->data = strdup((const char *)uv_req->ptr); 1903 if (!req->data) { 1904 req->failed = 1; 1905 req->error_msg = strdup("Out of memory"); 1906 req->completed = 1; 1907 complete_request(req); 1908 return; 1909 } 1910 1911 req->data_len = strlen(req->data); 1912 req->completed = 1; 1913 complete_request(req); 1914} 1915 1916static ant_value_t create_dirent_object(ant_t *js, const char *name, size_t name_len, uv_dirent_type_t type) { 1917 ant_value_t obj = js_newobj(js); 1918 js_set_proto(obj, g_dirent_proto); 1919 js_set(js, obj, "name", js_mkstr(js, name, name_len)); 1920 js_set_slot(obj, SLOT_DATA, tov((double)type)); 1921 return obj; 1922} 1923 1924static void on_mkdtemp_complete(uv_fs_t *uv_req) { 1925 fs_request_t *req = (fs_request_t *)uv_req->data; 1926 1927 if (uv_req->result < 0) { 1928 fs_request_fail(req, (int)uv_req->result); 1929 req->completed = 1; 1930 complete_request(req); 1931 uv_fs_req_cleanup(uv_req); 1932 return; 1933 } 1934 1935 req->data = strdup(uv_req->path); 1936 if (!req->data) { 1937 req->failed = 1; 1938 req->error_msg = strdup("Out of memory"); 1939 req->completed = 1; 1940 complete_request(req); 1941 uv_fs_req_cleanup(uv_req); 1942 return; 1943 } 1944 1945 req->data_len = strlen(req->data); 1946 req->completed = 1; 1947 uv_fs_req_cleanup(uv_req); 1948 complete_request(req); 1949} 1950 1951static void on_exists_complete(uv_fs_t *uv_req) { 1952 fs_request_t *req = (fs_request_t *)uv_req->data; 1953 ant_value_t result = js_bool(uv_req->result >= 0); 1954 1955 req->completed = 1; 1956 js_resolve_promise(req->js, req->promise, result); 1957 remove_pending_request(req); 1958 free_fs_request(req); 1959} 1960 1961static void on_access_complete(uv_fs_t *uv_req) { 1962 fs_request_t *req = (fs_request_t *)uv_req->data; 1963 1964 if (uv_req->result < 0) { 1965 fs_request_fail(req, (int)uv_req->result); 1966 req->completed = 1; 1967 complete_request(req); 1968 return; 1969 } 1970 1971 req->completed = 1; 1972 js_resolve_promise(req->js, req->promise, js_mkundef()); 1973 remove_pending_request(req); 1974 free_fs_request(req); 1975} 1976 1977static void on_chmod_complete(uv_fs_t *uv_req) { 1978 fs_request_t *req = (fs_request_t *)uv_req->data; 1979 1980 if (uv_req->result < 0) { 1981 fs_request_fail(req, (int)uv_req->result); 1982 req->completed = 1; 1983 complete_request(req); 1984 return; 1985 } 1986 1987 req->completed = 1; 1988 js_resolve_promise(req->js, req->promise, js_mkundef()); 1989 remove_pending_request(req); 1990 free_fs_request(req); 1991} 1992 1993static void on_readdir_complete(uv_fs_t *uv_req) { 1994 fs_request_t *req = (fs_request_t *)uv_req->data; 1995 1996 if (uv_req->result < 0) { 1997 fs_request_fail(req, (int)uv_req->result); 1998 req->completed = 1; 1999 complete_request(req); 2000 return; 2001 } 2002 2003 ant_value_t arr = js_mkarr(req->js); 2004 uv_dirent_t dirent; 2005 2006 while (uv_fs_scandir_next(uv_req, &dirent) != UV_EOF) { 2007 if (req->with_file_types) { 2008 ant_value_t entry = create_dirent_object(req->js, dirent.name, strlen(dirent.name), dirent.type); 2009 js_arr_push(req->js, arr, entry); 2010 } else { 2011 ant_value_t name = js_mkstr(req->js, dirent.name, strlen(dirent.name)); 2012 js_arr_push(req->js, arr, name); 2013 }} 2014 2015 req->completed = 1; 2016 js_resolve_promise(req->js, req->promise, arr); 2017 remove_pending_request(req); 2018 free_fs_request(req); 2019} 2020 2021static ant_value_t builtin_fs_readFileSync(ant_t *js, ant_value_t *args, int nargs) { 2022 if (nargs < 1) return js_mkerr(js, "readFileSync() requires a path argument"); 2023 2024 ant_value_t path_val = fs_coerce_path(js, args[0]); 2025 if (vtype(path_val) != T_STR) return js_mkerr(js, "readFileSync() path must be a string or URL"); 2026 2027 size_t path_len; 2028 char *path = js_getstr(js, path_val, &path_len); 2029 if (!path) return js_mkerr(js, "Failed to get path string"); 2030 2031 char *path_cstr = strndup(path, path_len); 2032 if (!path_cstr) return js_mkerr(js, "Out of memory"); 2033 2034 FILE *file = fopen(path_cstr, "rb"); 2035 if (!file) { 2036 ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); 2037 free(path_cstr); 2038 return err; 2039 } 2040 2041 fseek(file, 0, SEEK_END); 2042 long file_size = ftell(file); 2043 fseek(file, 0, SEEK_SET); 2044 2045 if (file_size < 0) { 2046 fclose(file); 2047 free(path_cstr); 2048 return js_mkerr(js, "Failed to get file size"); 2049 } 2050 2051 char *data = malloc(file_size + 1); 2052 if (!data) { 2053 fclose(file); 2054 free(path_cstr); 2055 return js_mkerr(js, "Out of memory"); 2056 } 2057 2058 size_t bytes_read = fread(data, 1, file_size, file); 2059 fclose(file); 2060 free(path_cstr); 2061 2062 if (bytes_read != (size_t)file_size) { 2063 free(data); 2064 return js_mkerr(js, "Failed to read entire file"); 2065 } 2066 2067 fs_encoding_t enc = (nargs > 1) ? parse_encoding(js, args[1]) : FS_ENC_NONE; 2068 ant_value_t result = (enc != FS_ENC_NONE) 2069 ? encode_data(js, data, file_size, enc) 2070 : fs_read_to_uint8array(js, data, file_size); 2071 2072 free(data); 2073 return result; 2074} 2075 2076static ant_value_t builtin_fs_readBytesSync(ant_t *js, ant_value_t *args, int nargs) { 2077 if (nargs < 1) return js_mkerr(js, "readBytesSync() requires a path argument"); 2078 if (vtype(args[0]) != T_STR) return js_mkerr(js, "readBytesSync() path must be a string"); 2079 2080 size_t path_len; 2081 char *path = js_getstr(js, args[0], &path_len); 2082 if (!path) return js_mkerr(js, "Failed to get path string"); 2083 2084 char *path_cstr = strndup(path, path_len); 2085 if (!path_cstr) return js_mkerr(js, "Out of memory"); 2086 2087 FILE *file = fopen(path_cstr, "rb"); 2088 if (!file) { 2089 ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); 2090 free(path_cstr); 2091 return err; 2092 } 2093 2094 fseek(file, 0, SEEK_END); 2095 long file_size = ftell(file); 2096 fseek(file, 0, SEEK_SET); 2097 2098 if (file_size < 0) { 2099 fclose(file); 2100 free(path_cstr); 2101 return js_mkerr(js, "Failed to get file size"); 2102 } 2103 2104 char *data = malloc(file_size); 2105 if (!data) { 2106 fclose(file); 2107 free(path_cstr); 2108 return js_mkerr(js, "Out of memory"); 2109 } 2110 2111 size_t bytes_read = fread(data, 1, file_size, file); 2112 fclose(file); 2113 free(path_cstr); 2114 2115 if (bytes_read != (size_t)file_size) { 2116 free(data); 2117 return js_mkerr(js, "Failed to read entire file"); 2118 } 2119 2120 ant_value_t result = js_mkstr(js, data, file_size); 2121 free(data); 2122 2123 return result; 2124} 2125 2126static ant_value_t builtin_fs_readFile(ant_t *js, ant_value_t *args, int nargs) { 2127 if (nargs < 1) return js_mkerr(js, "readFile() requires a path argument"); 2128 2129 ant_value_t path_val = fs_coerce_path(js, args[0]); 2130 if (vtype(path_val) != T_STR) return js_mkerr(js, "readFile() path must be a string or URL"); 2131 2132 size_t path_len; 2133 char *path = js_getstr(js, path_val, &path_len); 2134 if (!path) return js_mkerr(js, "Failed to get path string"); 2135 2136 2137 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2138 if (!req) return js_mkerr(js, "Out of memory"); 2139 2140 req->js = js; 2141 req->op_type = FS_OP_READ; 2142 req->encoding = (nargs > 1) ? parse_encoding(js, args[1]) : FS_ENC_NONE; 2143 req->promise = js_mkpromise(js); 2144 req->path = strndup(path, path_len); 2145 req->uv_req.data = req; 2146 2147 utarray_push_back(pending_requests, &req); 2148 int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, O_RDONLY, 0, on_open_for_read); 2149 2150 if (result < 0) { 2151 fs_request_fail(req, result); 2152 req->completed = 1; 2153 complete_request(req); 2154 } 2155 2156 return req->promise; 2157} 2158 2159static ant_value_t builtin_fs_readBytes(ant_t *js, ant_value_t *args, int nargs) { 2160 if (nargs < 1) return js_mkerr(js, "readBytes() requires a path argument"); 2161 2162 if (vtype(args[0]) != T_STR) return js_mkerr(js, "readBytes() path must be a string"); 2163 2164 size_t path_len; 2165 char *path = js_getstr(js, args[0], &path_len); 2166 if (!path) return js_mkerr(js, "Failed to get path string"); 2167 2168 2169 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2170 if (!req) return js_mkerr(js, "Out of memory"); 2171 2172 req->js = js; 2173 req->op_type = FS_OP_READ_BYTES; 2174 req->promise = js_mkpromise(js); 2175 req->path = strndup(path, path_len); 2176 req->uv_req.data = req; 2177 2178 utarray_push_back(pending_requests, &req); 2179 int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, O_RDONLY, 0, on_open_for_read); 2180 2181 if (result < 0) { 2182 fs_request_fail(req, result); 2183 req->completed = 1; 2184 complete_request(req); 2185 } 2186 2187 return req->promise; 2188} 2189 2190static ant_value_t fs_write_file_sync_impl( 2191 ant_t *js, 2192 ant_value_t *args, 2193 int nargs, 2194 const char *fn_name, 2195 const char *mode 2196) { 2197 if (nargs < 2) return js_mkerr(js, "%s() requires path and data arguments", fn_name); 2198 if (vtype(args[0]) != T_STR) return js_mkerr(js, "%s() path must be a string", fn_name); 2199 if (vtype(args[1]) != T_STR) return js_mkerr(js, "%s() data must be a string", fn_name); 2200 2201 size_t path_len, data_len; 2202 char *path = js_getstr(js, args[0], &path_len); 2203 char *data = js_getstr(js, args[1], &data_len); 2204 if (!path || !data) return js_mkerr(js, "Failed to get arguments"); 2205 2206 char *path_cstr = strndup(path, path_len); 2207 if (!path_cstr) return js_mkerr(js, "Out of memory"); 2208 2209 FILE *file = fopen(path_cstr, mode); 2210 if (!file) { 2211 ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); 2212 free(path_cstr); 2213 return err; 2214 } 2215 2216 size_t bytes_written = fwrite(data, 1, data_len, file); 2217 fclose(file); 2218 free(path_cstr); 2219 2220 if (bytes_written != data_len) { 2221 return js_mkerr(js, "Failed to write entire file"); 2222 } 2223 2224 return js_mkundef(); 2225} 2226 2227static ant_value_t builtin_fs_writeFileSync(ant_t *js, ant_value_t *args, int nargs) { 2228 return fs_write_file_sync_impl(js, args, nargs, "writeFileSync", "wb"); 2229} 2230 2231static ant_value_t builtin_fs_copyFileSync(ant_t *js, ant_value_t *args, int nargs) { 2232 if (nargs < 2) return js_mkerr(js, "copyFileSync() requires src and dest arguments"); 2233 2234 if (vtype(args[0]) != T_STR) return js_mkerr(js, "copyFileSync() src must be a string"); 2235 if (vtype(args[1]) != T_STR) return js_mkerr(js, "copyFileSync() dest must be a string"); 2236 2237 size_t src_len, dest_len; 2238 char *src = js_getstr(js, args[0], &src_len); 2239 char *dest = js_getstr(js, args[1], &dest_len); 2240 2241 if (!src || !dest) return js_mkerr(js, "Failed to get arguments"); 2242 2243 char *src_cstr = strndup(src, src_len); 2244 char *dest_cstr = strndup(dest, dest_len); 2245 if (!src_cstr || !dest_cstr) { 2246 free(src_cstr); 2247 free(dest_cstr); 2248 return js_mkerr(js, "Out of memory"); 2249 } 2250 2251 FILE *in = fopen(src_cstr, "rb"); 2252 if (!in) { 2253 ant_value_t err = fs_mk_errno_error(js, errno, "copyfile", src_cstr, dest_cstr); 2254 free(src_cstr); free(dest_cstr); 2255 return err; 2256 } 2257 2258 FILE *out = fopen(dest_cstr, "wb"); 2259 if (!out) { 2260 ant_value_t err = fs_mk_errno_error(js, errno, "copyfile", src_cstr, dest_cstr); 2261 fclose(in); 2262 free(src_cstr); free(dest_cstr); 2263 return err; 2264 } 2265 2266 char buf[8192]; 2267 size_t n; 2268 2269 while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { 2270 if (fwrite(buf, 1, n, out) != n) { 2271 ant_value_t err = fs_mk_errno_error(js, errno ? errno : EIO, "copyfile", src_cstr, dest_cstr); 2272 fclose(in); fclose(out); 2273 free(src_cstr); free(dest_cstr); 2274 return err; 2275 }} 2276 2277 fclose(in); fclose(out); 2278 free(src_cstr); free(dest_cstr); 2279 2280 return js_mkundef(); 2281} 2282 2283typedef struct { 2284 bool recursive; 2285 bool force; 2286 bool error_on_exist; 2287} fs_cp_options_t; 2288 2289static void fs_parse_cp_options(ant_t *js, ant_value_t value, fs_cp_options_t *opts) { 2290 opts->recursive = false; 2291 opts->force = true; 2292 opts->error_on_exist = false; 2293 2294 if (vtype(value) != T_OBJ) return; 2295 2296 ant_value_t recursive = js_get(js, value, "recursive"); 2297 ant_value_t force = js_get(js, value, "force"); 2298 ant_value_t error_on_exist = js_get(js, value, "errorOnExist"); 2299 2300 if (!is_undefined(recursive)) opts->recursive = js_truthy(js, recursive); 2301 if (!is_undefined(force)) opts->force = js_truthy(js, force); 2302 if (!is_undefined(error_on_exist)) opts->error_on_exist = js_truthy(js, error_on_exist); 2303} 2304 2305static char *fs_join_path(const char *base, const char *name) { 2306 size_t base_len = strlen(base); 2307 size_t name_len = strlen(name); 2308 bool need_sep = base_len > 0 && base[base_len - 1] != '/'; 2309 size_t total_len = base_len + (need_sep ? 1 : 0) + name_len; 2310 2311 char *joined = calloc(total_len + 1, 1); 2312 if (!joined) return NULL; 2313 2314 memcpy(joined, base, base_len); 2315 if (need_sep) joined[base_len++] = '/'; 2316 memcpy(joined + base_len, name, name_len); 2317 joined[total_len] = '\0'; 2318 return joined; 2319} 2320 2321static ant_value_t fs_copy_file_sync_impl( 2322 ant_t *js, 2323 const char *src_cstr, 2324 const char *dest_cstr, 2325 const fs_cp_options_t *opts, 2326 const char *op_name 2327) { 2328 struct stat dest_st; 2329 if (!opts->force && stat(dest_cstr, &dest_st) == 0) { 2330 if (opts->error_on_exist) return fs_mk_errno_error(js, EEXIST, op_name, src_cstr, dest_cstr); 2331 return js_mkundef(); 2332 } 2333 2334 FILE *in = fopen(src_cstr, "rb"); 2335 if (!in) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2336 2337 FILE *out = fopen(dest_cstr, "wb"); 2338 if (!out) { 2339 ant_value_t err = fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2340 fclose(in); 2341 return err; 2342 } 2343 2344 char buf[8192]; 2345 size_t n = 0; 2346 while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { 2347 if (fwrite(buf, 1, n, out) != n) { 2348 ant_value_t err = fs_mk_errno_error(js, errno ? errno : EIO, op_name, src_cstr, dest_cstr); 2349 fclose(in); 2350 fclose(out); 2351 return err; 2352 }} 2353 2354 fclose(in); 2355 fclose(out); 2356 2357 return js_mkundef(); 2358} 2359 2360static ant_value_t builtin_fs_copyFile(ant_t *js, ant_value_t *args, int nargs) { 2361 if (nargs < 2) return js_mkerr(js, "copyFile() requires src and dest arguments"); 2362 if (vtype(args[0]) != T_STR) return js_mkerr(js, "copyFile() src must be a string"); 2363 if (vtype(args[1]) != T_STR) return js_mkerr(js, "copyFile() dest must be a string"); 2364 2365 size_t src_len = 0, dest_len = 0; 2366 const char *src = js_getstr(js, args[0], &src_len); 2367 const char *dest = js_getstr(js, args[1], &dest_len); 2368 if (!src || !dest) return js_mkerr(js, "Failed to get arguments"); 2369 2370 char *src_cstr = strndup(src, src_len); 2371 char *dest_cstr = strndup(dest, dest_len); 2372 if (!src_cstr || !dest_cstr) { 2373 free(src_cstr); 2374 free(dest_cstr); 2375 return js_mkerr(js, "Out of memory"); 2376 } 2377 2378 fs_cp_options_t opts = { 2379 .recursive = false, 2380 .force = true, 2381 .error_on_exist = false, 2382 }; 2383 2384 ant_value_t promise = js_mkpromise(js); 2385 ant_value_t result = fs_copy_file_sync_impl(js, src_cstr, dest_cstr, &opts, "copyFile"); 2386 free(src_cstr); 2387 free(dest_cstr); 2388 2389 if (is_err(result)) js_reject_promise(js, promise, result); 2390 else js_resolve_promise(js, promise, js_mkundef()); 2391 2392 return promise; 2393} 2394 2395static ant_value_t fs_copy_path_sync_impl( 2396 ant_t *js, 2397 const char *src_cstr, 2398 const char *dest_cstr, 2399 const fs_cp_options_t *opts, 2400 const char *op_name 2401) { 2402 struct stat src_st; 2403 if (stat(src_cstr, &src_st) != 0) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2404 2405 if ((src_st.st_mode & S_IFMT) == S_IFDIR) { 2406 if (!opts->recursive) { 2407 return js_mkerr(js, "%s() recursive option is required to copy directories", op_name); 2408 } 2409 2410 struct stat dest_st; 2411 if (stat(dest_cstr, &dest_st) != 0) { 2412 if (errno != ENOENT) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2413 #ifdef _WIN32 2414 if (_mkdir(dest_cstr) != 0) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2415 #else 2416 if (mkdir(dest_cstr, (mode_t)(src_st.st_mode & 0777)) != 0) { 2417 return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2418 } 2419 #endif 2420 } else if ((dest_st.st_mode & S_IFMT) != S_IFDIR) { 2421 return fs_mk_errno_error(js, EEXIST, op_name, src_cstr, dest_cstr); 2422 } 2423 2424 uv_fs_t req; 2425 int rc = uv_fs_scandir(NULL, &req, src_cstr, 0, NULL); 2426 if (rc < 0) { 2427 ant_value_t err = fs_mk_uv_error(js, rc, "scandir", src_cstr, dest_cstr); 2428 uv_fs_req_cleanup(&req); 2429 return err; 2430 } 2431 2432 uv_dirent_t dirent; 2433 ant_value_t result = js_mkundef(); 2434 while (uv_fs_scandir_next(&req, &dirent) != UV_EOF) { 2435 if ( 2436 strcmp(dirent.name, ".") == 0 || 2437 strcmp(dirent.name, "..") == 0 2438 ) continue; 2439 2440 char *child_src = fs_join_path(src_cstr, dirent.name); 2441 char *child_dest = fs_join_path(dest_cstr, dirent.name); 2442 if (!child_src || !child_dest) { 2443 free(child_src); 2444 free(child_dest); 2445 result = js_mkerr(js, "Out of memory"); 2446 break; 2447 } 2448 2449 result = fs_copy_path_sync_impl(js, child_src, child_dest, opts, op_name); 2450 free(child_src); 2451 free(child_dest); 2452 if (is_err(result)) break; 2453 } 2454 2455 uv_fs_req_cleanup(&req); 2456 return result; 2457 } 2458 2459 return fs_copy_file_sync_impl(js, src_cstr, dest_cstr, opts, op_name); 2460} 2461 2462static ant_value_t fs_cp_sync_common( 2463 ant_t *js, 2464 ant_value_t *args, 2465 int nargs, 2466 const char *fn_name 2467) { 2468 if (nargs < 2) return js_mkerr(js, "%s() requires src and dest arguments", fn_name); 2469 if (vtype(args[0]) != T_STR) return js_mkerr(js, "%s() src must be a string", fn_name); 2470 if (vtype(args[1]) != T_STR) return js_mkerr(js, "%s() dest must be a string", fn_name); 2471 2472 size_t src_len = 0, dest_len = 0; 2473 const char *src = js_getstr(js, args[0], &src_len); 2474 const char *dest = js_getstr(js, args[1], &dest_len); 2475 if (!src || !dest) return js_mkerr(js, "Failed to get arguments"); 2476 2477 fs_cp_options_t opts; 2478 fs_parse_cp_options(js, nargs >= 3 ? args[2] : js_mkundef(), &opts); 2479 2480 char *src_cstr = strndup(src, src_len); 2481 char *dest_cstr = strndup(dest, dest_len); 2482 if (!src_cstr || !dest_cstr) { 2483 free(src_cstr); 2484 free(dest_cstr); 2485 return js_mkerr(js, "Out of memory"); 2486 } 2487 2488 ant_value_t result = fs_copy_path_sync_impl(js, src_cstr, dest_cstr, &opts, fn_name); 2489 free(src_cstr); 2490 free(dest_cstr); 2491 return result; 2492} 2493 2494static ant_value_t builtin_fs_cpSync(ant_t *js, ant_value_t *args, int nargs) { 2495 return fs_cp_sync_common(js, args, nargs, "cpSync"); 2496} 2497 2498static ant_value_t builtin_fs_cp(ant_t *js, ant_value_t *args, int nargs) { 2499 ant_value_t promise = js_mkpromise(js); 2500 ant_value_t result = fs_cp_sync_common(js, args, nargs, "cp"); 2501 if (is_err(result)) js_reject_promise(js, promise, result); 2502 else js_resolve_promise(js, promise, js_mkundef()); 2503 return promise; 2504} 2505 2506static ant_value_t builtin_fs_renameSync(ant_t *js, ant_value_t *args, int nargs) { 2507 if (nargs < 2) return js_mkerr(js, "renameSync() requires oldPath and newPath arguments"); 2508 2509 if (vtype(args[0]) != T_STR) return js_mkerr(js, "renameSync() oldPath must be a string"); 2510 if (vtype(args[1]) != T_STR) return js_mkerr(js, "renameSync() newPath must be a string"); 2511 2512 size_t old_len, new_len; 2513 char *old_path = js_getstr(js, args[0], &old_len); 2514 char *new_path = js_getstr(js, args[1], &new_len); 2515 2516 if (!old_path || !new_path) return js_mkerr(js, "Failed to get arguments"); 2517 2518 char *old_cstr = strndup(old_path, old_len); 2519 char *new_cstr = strndup(new_path, new_len); 2520 if (!old_cstr || !new_cstr) { 2521 free(old_cstr); 2522 free(new_cstr); 2523 return js_mkerr(js, "Out of memory"); 2524 } 2525 2526 int result = rename(old_cstr, new_cstr); 2527 2528 if (result != 0) { 2529 ant_value_t err = fs_mk_errno_error(js, errno, "rename", old_cstr, new_cstr); 2530 free(old_cstr); 2531 free(new_cstr); 2532 return err; 2533 } 2534 2535 free(old_cstr); 2536 free(new_cstr); 2537 2538 return js_mkundef(); 2539} 2540 2541static ant_value_t builtin_fs_rename(ant_t *js, ant_value_t *args, int nargs) { 2542 if (nargs < 2) return js_mkerr(js, "rename() requires oldPath and newPath arguments"); 2543 if (vtype(args[0]) != T_STR) return js_mkerr(js, "rename() oldPath must be a string"); 2544 if (vtype(args[1]) != T_STR) return js_mkerr(js, "rename() newPath must be a string"); 2545 2546 size_t old_len = 0, new_len = 0; 2547 const char *old_path = js_getstr(js, args[0], &old_len); 2548 const char *new_path = js_getstr(js, args[1], &new_len); 2549 if (!old_path || !new_path) return js_mkerr(js, "Failed to get arguments"); 2550 2551 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2552 if (!req) return js_mkerr(js, "Out of memory"); 2553 2554 req->js = js; 2555 req->op_type = FS_OP_RENAME; 2556 req->promise = js_mkpromise(js); 2557 req->path = strndup(old_path, old_len); 2558 req->path2 = strndup(new_path, new_len); 2559 req->uv_req.data = req; 2560 2561 if (!req->path || !req->path2) { 2562 free_fs_request(req); 2563 return js_mkerr(js, "Out of memory"); 2564 } 2565 2566 utarray_push_back(pending_requests, &req); 2567 int result = uv_fs_rename(uv_default_loop(), &req->uv_req, req->path, req->path2, on_rename_complete); 2568 2569 if (result < 0) { 2570 fs_request_fail(req, result); 2571 req->completed = 1; 2572 complete_request(req); 2573 } 2574 2575 return req->promise; 2576} 2577 2578static double fs_time_arg_to_seconds(ant_t *js, ant_value_t v) { 2579 if (is_date_instance(v)) { 2580 ant_value_t t = js_get_slot(js_as_obj(v), SLOT_DATA); 2581 return (vtype(t) == T_NUM) ? js_getnum(t) / 1000.0 : 0.0; 2582 } 2583 return js_to_number(js, v); 2584} 2585 2586static ant_value_t builtin_fs_utimesSync(ant_t *js, ant_value_t *args, int nargs) { 2587 if (nargs < 3) return js_mkerr(js, "utimesSync() requires path, atime, and mtime"); 2588 if (vtype(args[0]) != T_STR) return js_mkerr(js, "utimesSync() path must be a string"); 2589 2590 const char *path = js_str(js, args[0]); 2591 double atime = fs_time_arg_to_seconds(js, args[1]); 2592 double mtime = fs_time_arg_to_seconds(js, args[2]); 2593 2594 uv_fs_t req; 2595 int rc = uv_fs_utime(NULL, &req, path, atime, mtime, NULL); 2596 uv_fs_req_cleanup(&req); 2597 2598 if (rc < 0) return js_mkerr(js, "utimesSync: %s", uv_strerror(rc)); 2599 return js_mkundef(); 2600} 2601 2602static ant_value_t builtin_fs_utimes(ant_t *js, ant_value_t *args, int nargs) { 2603 return builtin_fs_utimesSync(js, args, nargs); 2604} 2605 2606static ant_value_t builtin_fs_futimesSync(ant_t *js, ant_value_t *args, int nargs) { 2607 if (nargs < 3) return js_mkerr(js, "futimesSync() requires fd, atime, and mtime"); 2608 int fd = (int)js_to_number(js, args[0]); 2609 double atime = fs_time_arg_to_seconds(js, args[1]); 2610 double mtime = fs_time_arg_to_seconds(js, args[2]); 2611 2612 uv_fs_t req; 2613 int rc = uv_fs_futime(NULL, &req, fd, atime, mtime, NULL); 2614 uv_fs_req_cleanup(&req); 2615 2616 if (rc < 0) return js_mkerr(js, "futimesSync: %s", uv_strerror(rc)); 2617 return js_mkundef(); 2618} 2619 2620static ant_value_t builtin_fs_futimes(ant_t *js, ant_value_t *args, int nargs) { 2621 return builtin_fs_futimesSync(js, args, nargs); 2622} 2623 2624static ant_value_t builtin_fs_appendFileSync(ant_t *js, ant_value_t *args, int nargs) { 2625 return fs_write_file_sync_impl(js, args, nargs, "appendFileSync", "ab"); 2626} 2627 2628static ant_value_t builtin_fs_appendFile(ant_t *js, ant_value_t *args, int nargs) { 2629 ant_value_t promise = js_mkpromise(js); 2630 ant_value_t result = fs_write_file_sync_impl(js, args, nargs, "appendFile", "ab"); 2631 if (is_err(result)) js_reject_promise(js, promise, result); 2632 else js_resolve_promise(js, promise, js_mkundef()); 2633 return promise; 2634} 2635 2636static ant_value_t builtin_fs_writeFile(ant_t *js, ant_value_t *args, int nargs) { 2637 if (nargs < 2) return js_mkerr(js, "writeFile() requires path and data arguments"); 2638 2639 if (vtype(args[0]) != T_STR) return js_mkerr(js, "writeFile() path must be a string"); 2640 if (vtype(args[1]) != T_STR) return js_mkerr(js, "writeFile() data must be a string"); 2641 2642 size_t path_len, data_len; 2643 char *path = js_getstr(js, args[0], &path_len); 2644 char *data = js_getstr(js, args[1], &data_len); 2645 2646 if (!path || !data) return js_mkerr(js, "Failed to get arguments"); 2647 2648 2649 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2650 if (!req) return js_mkerr(js, "Out of memory"); 2651 2652 req->js = js; 2653 req->op_type = FS_OP_WRITE; 2654 req->promise = js_mkpromise(js); 2655 req->path = strndup(path, path_len); 2656 req->data = malloc(data_len); 2657 if (!req->data) { 2658 free(req->path); 2659 free(req); 2660 return js_mkerr(js, "Out of memory"); 2661 } 2662 2663 memcpy(req->data, data, data_len); 2664 req->data_len = data_len; 2665 req->uv_req.data = req; 2666 2667 utarray_push_back(pending_requests, &req); 2668 int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, O_WRONLY | O_CREAT | O_TRUNC, 0644, on_open_for_write); 2669 2670 if (result < 0) { 2671 fs_request_fail(req, result); 2672 req->completed = 1; 2673 complete_request(req); 2674 } 2675 2676 return req->promise; 2677} 2678 2679static ant_value_t builtin_fs_unlinkSync(ant_t *js, ant_value_t *args, int nargs) { 2680 if (nargs < 1) return js_mkerr(js, "unlinkSync() requires a path argument"); 2681 2682 if (vtype(args[0]) != T_STR) return js_mkerr(js, "unlinkSync() path must be a string"); 2683 2684 size_t path_len; 2685 char *path = js_getstr(js, args[0], &path_len); 2686 if (!path) return js_mkerr(js, "Failed to get path string"); 2687 2688 char *path_cstr = strndup(path, path_len); 2689 if (!path_cstr) return js_mkerr(js, "Out of memory"); 2690 int result = unlink(path_cstr); 2691 2692 if (result != 0) { 2693 ant_value_t err = fs_mk_errno_error(js, errno, "unlink", path_cstr, NULL); 2694 free(path_cstr); 2695 return err; 2696 } 2697 2698 free(path_cstr); 2699 return js_mkundef(); 2700} 2701 2702static ant_value_t builtin_fs_unlink(ant_t *js, ant_value_t *args, int nargs) { 2703 if (nargs < 1) return js_mkerr(js, "unlink() requires a path argument"); 2704 2705 if (vtype(args[0]) != T_STR) return js_mkerr(js, "unlink() path must be a string"); 2706 2707 size_t path_len; 2708 char *path = js_getstr(js, args[0], &path_len); 2709 if (!path) return js_mkerr(js, "Failed to get path string"); 2710 2711 2712 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2713 if (!req) return js_mkerr(js, "Out of memory"); 2714 2715 req->js = js; 2716 req->op_type = FS_OP_UNLINK; 2717 req->promise = js_mkpromise(js); 2718 req->path = strndup(path, path_len); 2719 req->uv_req.data = req; 2720 2721 utarray_push_back(pending_requests, &req); 2722 int result = uv_fs_unlink(uv_default_loop(), &req->uv_req, req->path, on_unlink_complete); 2723 2724 if (result < 0) { 2725 fs_request_fail(req, result); 2726 req->completed = 1; 2727 complete_request(req); 2728 } 2729 2730 return req->promise; 2731} 2732 2733static ant_value_t builtin_fs_mkdirSync(ant_t *js, ant_value_t *args, int nargs) { 2734 if (nargs < 1) return js_mkerr(js, "mkdirSync() requires a path argument"); 2735 2736 if (vtype(args[0]) != T_STR) return js_mkerr(js, "mkdirSync() path must be a string"); 2737 2738 size_t path_len; 2739 char *path = js_getstr(js, args[0], &path_len); 2740 if (!path) return js_mkerr(js, "Failed to get path string"); 2741 2742 int mode = 0755; 2743 int recursive = 0; 2744 2745 if (nargs < 2) goto do_mkdir; 2746 2747 switch (vtype(args[1])) { 2748 case T_NUM: 2749 mode = (int)js_getnum(args[1]); 2750 break; 2751 case T_OBJ: { 2752 ant_value_t opt = args[1]; 2753 recursive = js_get(js, opt, "recursive") == js_true; 2754 ant_value_t mode_val = js_get(js, opt, "mode"); 2755 if (vtype(mode_val) == T_NUM) mode = (int)js_getnum(mode_val); 2756 break; 2757 } 2758 } 2759 2760do_mkdir: 2761 2762 char *path_cstr = strndup(path, path_len); 2763 if (!path_cstr) return js_mkerr(js, "Out of memory"); 2764 2765 int result; 2766 if (recursive) { 2767 result = mkdirp(path_cstr, (mode_t)mode); 2768 } else { 2769#ifdef _WIN32 2770 (void)mode; 2771 result = _mkdir(path_cstr); 2772#else 2773 result = mkdir(path_cstr, (mode_t)mode); 2774#endif 2775 } 2776 if (result != 0) { 2777 ant_value_t err = fs_mk_errno_error(js, errno, "mkdir", path_cstr, NULL); 2778 free(path_cstr); 2779 return err; 2780 } 2781 2782 free(path_cstr); 2783 2784 return js_mkundef(); 2785} 2786 2787static ant_value_t builtin_fs_mkdir(ant_t *js, ant_value_t *args, int nargs) { 2788 if (nargs < 1) return js_mkerr(js, "mkdir() requires a path argument"); 2789 2790 if (vtype(args[0]) != T_STR) return js_mkerr(js, "mkdir() path must be a string"); 2791 2792 size_t path_len; 2793 char *path = js_getstr(js, args[0], &path_len); 2794 if (!path) return js_mkerr(js, "Failed to get path string"); 2795 2796 int mode = 0755; 2797 int recursive = 0; 2798 if (nargs >= 2) { 2799 switch (vtype(args[1])) { 2800 case T_NUM: 2801 mode = (int)js_getnum(args[1]); 2802 break; 2803 case T_OBJ: { 2804 ant_value_t opt = args[1]; 2805 recursive = js_get(js, opt, "recursive") == js_true; 2806 ant_value_t mode_val = js_get(js, opt, "mode"); 2807 if (vtype(mode_val) == T_NUM) mode = (int)js_getnum(mode_val); 2808 break; 2809 }} 2810 } 2811 2812 if (recursive) { 2813 ant_value_t promise = js_mkpromise(js); 2814 char *path_cstr = strndup(path, path_len); 2815 2816 if (!path_cstr) return js_mkerr(js, "Out of memory"); 2817 int result = mkdirp(path_cstr, (mode_t)mode); 2818 2819 free(path_cstr); 2820 if (result != 0) { 2821 js_reject_promise(js, promise, js_mkerr(js, "Failed to create directory: %s", strerror(errno))); 2822 } else js_resolve_promise(js, promise, js_mkundef()); 2823 2824 return promise; 2825 } 2826 2827 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2828 if (!req) return js_mkerr(js, "Out of memory"); 2829 2830 req->js = js; 2831 req->op_type = FS_OP_MKDIR; 2832 req->promise = js_mkpromise(js); 2833 req->path = strndup(path, path_len); 2834 req->recursive = recursive; 2835 req->uv_req.data = req; 2836 2837 utarray_push_back(pending_requests, &req); 2838 int result = uv_fs_mkdir(uv_default_loop(), &req->uv_req, req->path, mode, on_mkdir_complete); 2839 2840 if (result < 0) { 2841 fs_request_fail(req, result); 2842 req->completed = 1; 2843 complete_request(req); 2844 } 2845 2846 return req->promise; 2847} 2848 2849static ant_value_t builtin_fs_mkdtempSync(ant_t *js, ant_value_t *args, int nargs) { 2850 if (nargs < 1 || vtype(args[0]) != T_STR) 2851 return js_mkerr(js, "mkdtempSync() requires a prefix string"); 2852 2853 size_t prefix_len; 2854 const char *prefix = js_getstr(js, args[0], &prefix_len); 2855 if (!prefix) return js_mkerr(js, "Failed to get prefix string"); 2856 2857 size_t tpl_len = prefix_len + 6; 2858 char *tpl = malloc(tpl_len + 1); 2859 if (!tpl) return js_mkerr(js, "Out of memory"); 2860 2861 memcpy(tpl, prefix, prefix_len); 2862 memcpy(tpl + prefix_len, "XXXXXX", 6); 2863 tpl[tpl_len] = '\0'; 2864 2865 char *result = mkdtemp(tpl); 2866 if (!result) { 2867 free(tpl); 2868 return js_mkerr(js, "mkdtempSync failed: %s", strerror(errno)); 2869 } 2870 2871 ant_value_t ret = js_mkstr(js, result, strlen(result)); 2872 free(tpl); 2873 return ret; 2874} 2875 2876static ant_value_t builtin_fs_mkdtemp(ant_t *js, ant_value_t *args, int nargs) { 2877 if (nargs < 1 || vtype(args[0]) != T_STR) 2878 return js_mkerr(js, "mkdtemp() requires a prefix string"); 2879 2880 size_t prefix_len; 2881 const char *prefix = js_getstr(js, args[0], &prefix_len); 2882 if (!prefix) return js_mkerr(js, "Failed to get prefix string"); 2883 2884 size_t tpl_len = prefix_len + 6; 2885 char *tpl = malloc(tpl_len + 1); 2886 if (!tpl) return js_mkerr(js, "Out of memory"); 2887 2888 memcpy(tpl, prefix, prefix_len); 2889 memcpy(tpl + prefix_len, "XXXXXX", 6); 2890 tpl[tpl_len] = '\0'; 2891 2892 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2893 if (!req) { free(tpl); return js_mkerr(js, "Out of memory"); } 2894 2895 req->js = js; 2896 req->op_type = FS_OP_MKDTEMP; 2897 req->promise = js_mkpromise(js); 2898 req->path = tpl; 2899 req->uv_req.data = req; 2900 2901 utarray_push_back(pending_requests, &req); 2902 int result = uv_fs_mkdtemp(uv_default_loop(), &req->uv_req, req->path, on_mkdtemp_complete); 2903 2904 if (result < 0) { 2905 fs_request_fail(req, result); 2906 req->completed = 1; 2907 complete_request(req); 2908 } 2909 2910 return req->promise; 2911} 2912 2913static ant_value_t builtin_fs_rmdirSync(ant_t *js, ant_value_t *args, int nargs) { 2914 if (nargs < 1) return js_mkerr(js, "rmdirSync() requires a path argument"); 2915 2916 if (vtype(args[0]) != T_STR) return js_mkerr(js, "rmdirSync() path must be a string"); 2917 2918 size_t path_len; 2919 char *path = js_getstr(js, args[0], &path_len); 2920 if (!path) return js_mkerr(js, "Failed to get path string"); 2921 2922 char *path_cstr = strndup(path, path_len); 2923 if (!path_cstr) return js_mkerr(js, "Out of memory"); 2924 2925#ifdef _WIN32 2926 int result = _rmdir(path_cstr); 2927#else 2928 int result = rmdir(path_cstr); 2929#endif 2930 if (result != 0) { 2931 ant_value_t err = fs_mk_errno_error(js, errno, "rmdir", path_cstr, NULL); 2932 free(path_cstr); 2933 return err; 2934 } 2935 2936 free(path_cstr); 2937 return js_mkundef(); 2938} 2939 2940static ant_value_t builtin_fs_rmdir(ant_t *js, ant_value_t *args, int nargs) { 2941 if (nargs < 1) return js_mkerr(js, "rmdir() requires a path argument"); 2942 2943 if (vtype(args[0]) != T_STR) return js_mkerr(js, "rmdir() path must be a string"); 2944 2945 size_t path_len; 2946 char *path = js_getstr(js, args[0], &path_len); 2947 if (!path) return js_mkerr(js, "Failed to get path string"); 2948 2949 2950 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2951 if (!req) return js_mkerr(js, "Out of memory"); 2952 2953 req->js = js; 2954 req->op_type = FS_OP_RMDIR; 2955 req->promise = js_mkpromise(js); 2956 req->path = strndup(path, path_len); 2957 req->uv_req.data = req; 2958 2959 utarray_push_back(pending_requests, &req); 2960 int result = uv_fs_rmdir(uv_default_loop(), &req->uv_req, req->path, on_rmdir_complete); 2961 2962 if (result < 0) { 2963 fs_request_fail(req, result); 2964 req->completed = 1; 2965 complete_request(req); 2966 } 2967 2968 return req->promise; 2969} 2970 2971static ant_value_t builtin_fs_rmSync(ant_t *js, ant_value_t *args, int nargs) { 2972 return fs_rm_impl(js, args, nargs, false); 2973} 2974 2975static ant_value_t builtin_fs_rm(ant_t *js, ant_value_t *args, int nargs) { 2976 return fs_rm_impl(js, args, nargs, true); 2977} 2978 2979static ant_value_t dirent_isFile(ant_t *js, ant_value_t *args, int nargs) { 2980 ant_value_t this = js_getthis(js); 2981 ant_value_t type_val = js_get_slot(this, SLOT_DATA); 2982 if (vtype(type_val) != T_NUM) return js_false; 2983 return js_bool((int)js_getnum(type_val) == UV_DIRENT_FILE); 2984} 2985 2986static ant_value_t dirent_isDirectory(ant_t *js, ant_value_t *args, int nargs) { 2987 ant_value_t this = js_getthis(js); 2988 ant_value_t type_val = js_get_slot(this, SLOT_DATA); 2989 if (vtype(type_val) != T_NUM) return js_false; 2990 return js_bool((int)js_getnum(type_val) == UV_DIRENT_DIR); 2991} 2992 2993static ant_value_t dirent_isSymbolicLink(ant_t *js, ant_value_t *args, int nargs) { 2994 ant_value_t this = js_getthis(js); 2995 ant_value_t type_val = js_get_slot(this, SLOT_DATA); 2996 if (vtype(type_val) != T_NUM) return js_false; 2997 return js_bool((int)js_getnum(type_val) == UV_DIRENT_LINK); 2998} 2999 3000static ant_value_t dirent_isBlockDevice(ant_t *js, ant_value_t *args, int nargs) { 3001 ant_value_t this = js_getthis(js); 3002 ant_value_t type_val = js_get_slot(this, SLOT_DATA); 3003 if (vtype(type_val) != T_NUM) return js_false; 3004 return js_bool((int)js_getnum(type_val) == UV_DIRENT_BLOCK); 3005} 3006 3007static ant_value_t dirent_isCharacterDevice(ant_t *js, ant_value_t *args, int nargs) { 3008 ant_value_t this = js_getthis(js); 3009 ant_value_t type_val = js_get_slot(this, SLOT_DATA); 3010 if (vtype(type_val) != T_NUM) return js_false; 3011 return js_bool((int)js_getnum(type_val) == UV_DIRENT_CHAR); 3012} 3013 3014static ant_value_t dirent_isFIFO(ant_t *js, ant_value_t *args, int nargs) { 3015 ant_value_t this = js_getthis(js); 3016 ant_value_t type_val = js_get_slot(this, SLOT_DATA); 3017 if (vtype(type_val) != T_NUM) return js_false; 3018 return js_bool((int)js_getnum(type_val) == UV_DIRENT_FIFO); 3019} 3020 3021static ant_value_t dirent_isSocket(ant_t *js, ant_value_t *args, int nargs) { 3022 ant_value_t this = js_getthis(js); 3023 ant_value_t type_val = js_get_slot(this, SLOT_DATA); 3024 if (vtype(type_val) != T_NUM) return js_false; 3025 return js_bool((int)js_getnum(type_val) == UV_DIRENT_SOCKET); 3026} 3027 3028static ant_value_t stat_isFile(ant_t *js, ant_value_t *args, int nargs) { 3029 ant_value_t this = js_getthis(js); 3030 ant_value_t mode_val = js_get_slot(this, SLOT_DATA); 3031 3032 if (vtype(mode_val) != T_NUM) return js_false; 3033 mode_t mode = (mode_t)js_getnum(mode_val); 3034 3035 return js_bool(S_ISREG(mode)); 3036} 3037 3038static ant_value_t stat_isDirectory(ant_t *js, ant_value_t *args, int nargs) { 3039 ant_value_t this = js_getthis(js); 3040 ant_value_t mode_val = js_get_slot(this, SLOT_DATA); 3041 3042 if (vtype(mode_val) != T_NUM) return js_false; 3043 mode_t mode = (mode_t)js_getnum(mode_val); 3044 3045 return js_bool(S_ISDIR(mode)); 3046} 3047 3048static ant_value_t stat_isSymbolicLink(ant_t *js, ant_value_t *args, int nargs) { 3049 ant_value_t this = js_getthis(js); 3050 ant_value_t mode_val = js_get_slot(this, SLOT_DATA); 3051 3052 if (vtype(mode_val) != T_NUM) return js_false; 3053 mode_t mode = (mode_t)js_getnum(mode_val); 3054 3055 return js_bool(S_ISLNK(mode)); 3056} 3057 3058static ant_value_t create_stats_object(ant_t *js, struct stat *st) { 3059 return fs_stats_object_from_posix(js, st); 3060} 3061 3062static const char *errno_to_code(int err_num) { 3063switch (err_num) { 3064 case ENOENT: return "ENOENT"; 3065 case EACCES: return "EACCES"; 3066 case ENOTDIR: return "ENOTDIR"; 3067 case ELOOP: return "ELOOP"; 3068 case ENAMETOOLONG: return "ENAMETOOLONG"; 3069 case EOVERFLOW: return "EOVERFLOW"; 3070 case EROFS: return "EROFS"; 3071 case ETXTBSY: return "ETXTBSY"; 3072 case EEXIST: return "EEXIST"; 3073 case ENOTEMPTY: return "ENOTEMPTY"; 3074 case EISDIR: return "EISDIR"; 3075 case EBUSY: return "EBUSY"; 3076 case EINVAL: return "EINVAL"; 3077 case EPERM: return "EPERM"; 3078 case EIO: return "EIO"; 3079 default: return "UNKNOWN"; 3080}} 3081 3082static ant_value_t fs_mk_syscall_error( 3083 ant_t *js, const char *code, double err_num, 3084 const char *message, const char *syscall, 3085 const char *path, const char *dest 3086) { 3087 ant_value_t props = js_mkobj(js); 3088 3089 if (!code) code = "UNKNOWN"; 3090 if (!message) message = "Unknown error"; 3091 3092 js_set(js, props, "code", js_mkstr(js, code, strlen(code))); 3093 js_set(js, props, "errno", js_mknum(err_num)); 3094 3095 if (syscall) js_set(js, props, "syscall", js_mkstr(js, syscall, strlen(syscall))); 3096 if (path) js_set(js, props, "path", js_mkstr(js, path, strlen(path))); 3097 if (dest) js_set(js, props, "dest", js_mkstr(js, dest, strlen(dest))); 3098 3099 if (path && dest) return js_mkerr_props( 3100 js, JS_ERR_GENERIC, 3101 props, "%s: %s, %s '%s' -> '%s'", 3102 code, message, syscall, path, dest 3103 ); 3104 3105 if (path) return js_mkerr_props( 3106 js, JS_ERR_GENERIC, 3107 props, "%s: %s, %s '%s'", 3108 code, message, syscall, path 3109 ); 3110 3111 return js_mkerr_props( 3112 js, JS_ERR_GENERIC, 3113 props, "%s: %s, %s", 3114 code, message, syscall 3115 ); 3116} 3117 3118static ant_value_t fs_mk_errno_error( 3119 ant_t *js, int err_num, 3120 const char *syscall, const char *path, const char *dest 3121) { 3122 int uv_code = uv_translate_sys_error(err_num); 3123 if (uv_code == 0) uv_code = -err_num; 3124 3125 return fs_mk_syscall_error( 3126 js, errno_to_code(err_num), 3127 (double)uv_code, 3128 uv_strerror(uv_code), 3129 syscall, path, dest 3130 ); 3131} 3132 3133static ant_value_t fs_mk_uv_error( 3134 ant_t *js, int uv_code, 3135 const char *syscall, const char *path, const char *dest 3136) { 3137 return fs_mk_syscall_error( 3138 js, uv_err_name(uv_code), 3139 (double)uv_code, 3140 uv_strerror(uv_code), 3141 syscall, path, dest 3142 ); 3143} 3144 3145static ant_value_t builtin_fs_statSync(ant_t *js, ant_value_t *args, int nargs) { 3146 if (nargs < 1) return js_mkerr(js, "statSync() requires a path argument"); 3147 if (vtype(args[0]) != T_STR) return js_mkerr(js, "statSync() path must be a string"); 3148 3149 size_t path_len; 3150 char *path = js_getstr(js, args[0], &path_len); 3151 if (!path) return js_mkerr(js, "Failed to get path string"); 3152 3153 char *path_cstr = strndup(path, path_len); 3154 if (!path_cstr) return js_mkerr(js, "Out of memory"); 3155 3156 struct stat st; 3157 int result = stat(path_cstr, &st); 3158 3159 if (result != 0) { 3160 ant_value_t err = fs_mk_errno_error(js, errno, "stat", path_cstr, NULL); 3161 free(path_cstr); return err; 3162 } 3163 3164 free(path_cstr); 3165 return create_stats_object(js, &st); 3166} 3167 3168static ant_value_t builtin_fs_stat(ant_t *js, ant_value_t *args, int nargs) { 3169 if (nargs < 1) return js_mkerr(js, "stat() requires a path argument"); 3170 if (vtype(args[0]) != T_STR) return js_mkerr(js, "stat() path must be a string"); 3171 3172 size_t path_len; 3173 char *path = js_getstr(js, args[0], &path_len); 3174 if (!path) return js_mkerr(js, "Failed to get path string"); 3175 3176 3177 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3178 if (!req) return js_mkerr(js, "Out of memory"); 3179 3180 req->js = js; 3181 req->op_type = FS_OP_STAT; 3182 req->promise = js_mkpromise(js); 3183 req->path = strndup(path, path_len); 3184 req->uv_req.data = req; 3185 3186 utarray_push_back(pending_requests, &req); 3187 int result = uv_fs_stat(uv_default_loop(), &req->uv_req, req->path, on_stat_complete); 3188 3189 if (result < 0) { 3190 fs_request_fail(req, result); 3191 req->completed = 1; 3192 complete_request(req); 3193 } 3194 3195 return req->promise; 3196} 3197 3198static ant_value_t builtin_fs_lstatSync(ant_t *js, ant_value_t *args, int nargs) { 3199 if (nargs < 1) return js_mkerr(js, "lstatSync() requires a path argument"); 3200 if (vtype(args[0]) != T_STR) return js_mkerr(js, "lstatSync() path must be a string"); 3201 3202 size_t path_len; 3203 char *path = js_getstr(js, args[0], &path_len); 3204 if (!path) return js_mkerr(js, "Failed to get path string"); 3205 3206 char *path_cstr = strndup(path, path_len); 3207 if (!path_cstr) return js_mkerr(js, "Out of memory"); 3208 3209 uv_fs_t req; 3210 int result = uv_fs_lstat(NULL, &req, path_cstr, NULL); 3211 3212 if (result < 0) { 3213 ant_value_t err = fs_mk_uv_error(js, result, "lstat", path_cstr, NULL); 3214 uv_fs_req_cleanup(&req); 3215 free(path_cstr); 3216 return err; 3217 } 3218 3219 ant_value_t stat_obj = fs_stats_object_from_uv(js, &req.statbuf); 3220 uv_fs_req_cleanup(&req); 3221 free(path_cstr); 3222 3223 return stat_obj; 3224} 3225 3226static ant_value_t builtin_fs_lstat(ant_t *js, ant_value_t *args, int nargs) { 3227 if (nargs < 1) return js_mkerr(js, "lstat() requires a path argument"); 3228 if (vtype(args[0]) != T_STR) return js_mkerr(js, "lstat() path must be a string"); 3229 3230 size_t path_len; 3231 char *path = js_getstr(js, args[0], &path_len); 3232 if (!path) return js_mkerr(js, "Failed to get path string"); 3233 3234 3235 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3236 if (!req) return js_mkerr(js, "Out of memory"); 3237 3238 req->js = js; 3239 req->op_type = FS_OP_STAT; 3240 req->promise = js_mkpromise(js); 3241 req->path = strndup(path, path_len); 3242 req->uv_req.data = req; 3243 3244 utarray_push_back(pending_requests, &req); 3245 int result = uv_fs_lstat(uv_default_loop(), &req->uv_req, req->path, on_stat_complete); 3246 3247 if (result < 0) { 3248 fs_request_fail(req, result); 3249 req->completed = 1; 3250 complete_request(req); 3251 } 3252 3253 return req->promise; 3254} 3255 3256static ant_value_t builtin_fs_fstatSync(ant_t *js, ant_value_t *args, int nargs) { 3257 if (nargs < 1) return js_mkerr(js, "fstatSync() requires an fd argument"); 3258 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "fstatSync() fd must be a number"); 3259 3260 uv_file fd = (uv_file)(int)js_getnum(args[0]); 3261 uv_fs_t req; 3262 int result = uv_fs_fstat(NULL, &req, fd, NULL); 3263 3264 if (result < 0) { 3265 ant_value_t err = fs_mk_uv_error(js, result, "fstat", NULL, NULL); 3266 uv_fs_req_cleanup(&req); 3267 return err; 3268 } 3269 3270 ant_value_t stat_obj = fs_stats_object_from_uv(js, &req.statbuf); 3271 uv_fs_req_cleanup(&req); 3272 return stat_obj; 3273} 3274 3275static ant_value_t builtin_fs_fstat(ant_t *js, ant_value_t *args, int nargs) { 3276 if (nargs < 1) return js_mkerr(js, "fstat() requires an fd argument"); 3277 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "fstat() fd must be a number"); 3278 3279 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3280 if (!req) return js_mkerr(js, "Out of memory"); 3281 3282 req->js = js; 3283 req->op_type = FS_OP_STAT; 3284 req->promise = js_mkpromise(js); 3285 req->fd = (uv_file)(int)js_getnum(args[0]); 3286 req->uv_req.data = req; 3287 3288 utarray_push_back(pending_requests, &req); 3289 int result = uv_fs_fstat(uv_default_loop(), &req->uv_req, req->fd, on_stat_complete); 3290 3291 if (result < 0) { 3292 fs_request_fail(req, result); 3293 req->completed = 1; 3294 complete_request(req); 3295 } 3296 3297 return req->promise; 3298} 3299 3300static ant_value_t builtin_fs_existsSync(ant_t *js, ant_value_t *args, int nargs) { 3301 if (nargs < 1) return js_mkerr(js, "existsSync() requires a path argument"); 3302 if (vtype(args[0]) != T_STR) return js_mkerr(js, "existsSync() path must be a string"); 3303 3304 size_t path_len; 3305 char *path = js_getstr(js, args[0], &path_len); 3306 if (!path) return js_mkerr(js, "Failed to get path string"); 3307 3308 char *path_cstr = strndup(path, path_len); 3309 if (!path_cstr) return js_mkerr(js, "Out of memory"); 3310 3311 struct stat st; 3312 int result = stat(path_cstr, &st); 3313 free(path_cstr); 3314 3315 return js_bool(result == 0); 3316} 3317 3318static ant_value_t builtin_fs_exists(ant_t *js, ant_value_t *args, int nargs) { 3319 if (nargs < 1) return js_mkerr(js, "exists() requires a path argument"); 3320 if (vtype(args[0]) != T_STR) return js_mkerr(js, "exists() path must be a string"); 3321 3322 size_t path_len; 3323 char *path = js_getstr(js, args[0], &path_len); 3324 if (!path) return js_mkerr(js, "Failed to get path string"); 3325 3326 3327 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3328 if (!req) return js_mkerr(js, "Out of memory"); 3329 3330 req->js = js; 3331 req->op_type = FS_OP_EXISTS; 3332 req->promise = js_mkpromise(js); 3333 req->path = strndup(path, path_len); 3334 req->uv_req.data = req; 3335 3336 utarray_push_back(pending_requests, &req); 3337 int result = uv_fs_stat(uv_default_loop(), &req->uv_req, req->path, on_exists_complete); 3338 3339 if (result < 0) { 3340 req->completed = 1; 3341 js_resolve_promise(req->js, req->promise, js_false); 3342 remove_pending_request(req); 3343 free_fs_request(req); 3344 } 3345 3346 return req->promise; 3347} 3348 3349static ant_value_t builtin_fs_accessSync(ant_t *js, ant_value_t *args, int nargs) { 3350 if (nargs < 1) return js_mkerr(js, "accessSync() requires a path argument"); 3351 if (vtype(args[0]) != T_STR) return js_mkerr(js, "accessSync() path must be a string"); 3352 3353 size_t path_len; 3354 char *path = js_getstr(js, args[0], &path_len); 3355 if (!path) return js_mkerr(js, "Failed to get path string"); 3356 3357 int mode = F_OK; 3358 if (nargs >= 2 && vtype(args[1]) == T_NUM) { 3359 mode = (int)js_getnum(args[1]); 3360 } 3361 3362 char *path_cstr = strndup(path, path_len); 3363 if (!path_cstr) return js_mkerr(js, "Out of memory"); 3364 int result = access(path_cstr, mode); 3365 3366 if (result != 0) { 3367 ant_value_t err = fs_mk_errno_error(js, errno, "access", path_cstr, NULL); 3368 free(path_cstr); return err; 3369 } 3370 3371 free(path_cstr); 3372 return js_mkundef(); 3373} 3374 3375static ant_value_t builtin_fs_chmodSync(ant_t *js, ant_value_t *args, int nargs) { 3376 if (nargs < 1) return js_mkerr(js, "chmodSync() requires a path argument"); 3377 if (nargs < 2) return js_mkerr(js, "chmodSync() requires a mode argument"); 3378 3379 ant_value_t path_val = fs_coerce_path(js, args[0]); 3380 if (vtype(path_val) != T_STR) return js_mkerr(js, "chmodSync() path must be a string or URL"); 3381 3382 mode_t mode = 0; 3383 if (!fs_parse_mode(js, args[1], &mode)) { 3384 return js_mkerr(js, "chmodSync() mode must be a number or octal string"); 3385 } 3386 3387 size_t path_len = 0; 3388 char *path = js_getstr(js, path_val, &path_len); 3389 if (!path) return js_mkerr(js, "Failed to get path string"); 3390 3391 char *path_cstr = strndup(path, path_len); 3392 if (!path_cstr) return js_mkerr(js, "Out of memory"); 3393 3394 uv_fs_t req; 3395 int result = uv_fs_chmod(uv_default_loop(), &req, path_cstr, mode, NULL); 3396 3397 if (result < 0) { 3398 ant_value_t err = fs_mk_uv_error(js, result, "chmod", path_cstr, NULL); 3399 uv_fs_req_cleanup(&req); 3400 free(path_cstr); 3401 return err; 3402 } 3403 3404 uv_fs_req_cleanup(&req); 3405 free(path_cstr); 3406 return js_mkundef(); 3407} 3408 3409static ant_value_t builtin_fs_chmod(ant_t *js, ant_value_t *args, int nargs) { 3410 if (nargs < 1) return js_mkerr(js, "chmod() requires a path argument"); 3411 if (nargs < 2) return js_mkerr(js, "chmod() requires a mode argument"); 3412 3413 ant_value_t path_val = fs_coerce_path(js, args[0]); 3414 if (vtype(path_val) != T_STR) return js_mkerr(js, "chmod() path must be a string or URL"); 3415 3416 mode_t mode = 0; 3417 if (!fs_parse_mode(js, args[1], &mode)) { 3418 return js_mkerr(js, "chmod() mode must be a number or octal string"); 3419 } 3420 3421 size_t path_len = 0; 3422 char *path = js_getstr(js, path_val, &path_len); 3423 if (!path) return js_mkerr(js, "Failed to get path string"); 3424 3425 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3426 if (!req) return js_mkerr(js, "Out of memory"); 3427 3428 req->js = js; 3429 req->op_type = FS_OP_CHMOD; 3430 req->promise = js_mkpromise(js); 3431 req->path = strndup(path, path_len); 3432 req->uv_req.data = req; 3433 3434 if (!req->path) { 3435 free_fs_request(req); 3436 return js_mkerr(js, "Out of memory"); 3437 } 3438 3439 utarray_push_back(pending_requests, &req); 3440 int result = uv_fs_chmod(uv_default_loop(), &req->uv_req, req->path, mode, on_chmod_complete); 3441 3442 if (result < 0) { 3443 fs_request_fail(req, result); 3444 req->completed = 1; 3445 complete_request(req); 3446 } 3447 3448 return req->promise; 3449} 3450 3451static ant_value_t builtin_fs_access(ant_t *js, ant_value_t *args, int nargs) { 3452 if (nargs < 1) return js_mkerr(js, "access() requires a path argument"); 3453 if (vtype(args[0]) != T_STR) return js_mkerr(js, "access() path must be a string"); 3454 3455 size_t path_len; 3456 char *path = js_getstr(js, args[0], &path_len); 3457 if (!path) return js_mkerr(js, "Failed to get path string"); 3458 3459 int mode = F_OK; 3460 if (nargs >= 2 && vtype(args[1]) == T_NUM) { 3461 mode = (int)js_getnum(args[1]); 3462 } 3463 3464 3465 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3466 if (!req) return js_mkerr(js, "Out of memory"); 3467 3468 req->js = js; 3469 req->op_type = FS_OP_ACCESS; 3470 req->promise = js_mkpromise(js); 3471 req->path = strndup(path, path_len); 3472 req->uv_req.data = req; 3473 3474 utarray_push_back(pending_requests, &req); 3475 int result = uv_fs_access(uv_default_loop(), &req->uv_req, req->path, mode, on_access_complete); 3476 3477 if (result < 0) { 3478 fs_request_fail(req, result); 3479 req->completed = 1; 3480 complete_request(req); 3481 } 3482 3483 return req->promise; 3484} 3485 3486static ant_value_t builtin_fs_realpathSync(ant_t *js, ant_value_t *args, int nargs) { 3487 if (nargs < 1) return js_mkerr(js, "realpathSync() requires a path argument"); 3488 3489 ant_value_t path_val = fs_coerce_path(js, args[0]); 3490 if (vtype(path_val) != T_STR) return js_mkerr(js, "realpathSync() path must be a string"); 3491 3492 size_t path_len = 0; 3493 const char *path = js_getstr(js, path_val, &path_len); 3494 if (!path) return js_mkerr(js, "Failed to get path string"); 3495 3496 char *resolved = realpath(path, NULL); 3497 if (!resolved) return js_mkerr(js, "realpathSync failed for '%s'", path); 3498 3499 ant_value_t result = js_mkstr(js, resolved, strlen(resolved)); 3500 free(resolved); 3501 return result; 3502} 3503 3504static ant_value_t builtin_fs_realpath(ant_t *js, ant_value_t *args, int nargs) { 3505 if (nargs < 1) return js_mkerr(js, "realpath() requires a path argument"); 3506 3507 ant_value_t path_val = fs_coerce_path(js, args[0]); 3508 if (vtype(path_val) != T_STR) return js_mkerr(js, "realpath() path must be a string"); 3509 3510 size_t path_len = 0; 3511 const char *path = js_getstr(js, path_val, &path_len); 3512 if (!path) return js_mkerr(js, "Failed to get path string"); 3513 3514 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3515 if (!req) return js_mkerr(js, "Out of memory"); 3516 3517 req->js = js; 3518 req->op_type = FS_OP_REALPATH; 3519 req->promise = js_mkpromise(js); 3520 req->path = strndup(path, path_len); 3521 req->uv_req.data = req; 3522 3523 if (!req->path) { 3524 free_fs_request(req); 3525 return js_mkerr(js, "Out of memory"); 3526 } 3527 3528 utarray_push_back(pending_requests, &req); 3529 int result = uv_fs_realpath(uv_default_loop(), &req->uv_req, req->path, on_realpath_complete); 3530 3531 if (result < 0) { 3532 fs_request_fail(req, result); 3533 req->completed = 1; 3534 complete_request(req); 3535 } 3536 3537 return req->promise; 3538} 3539 3540static ant_value_t builtin_fs_readlinkSync(ant_t *js, ant_value_t *args, int nargs) { 3541 if (nargs < 1) return js_mkerr(js, "readlinkSync() requires a path argument"); 3542 3543 ant_value_t path_val = fs_coerce_path(js, args[0]); 3544 if (vtype(path_val) != T_STR) return js_mkerr(js, "readlinkSync() path must be a string"); 3545 3546 size_t path_len = 0; 3547 const char *path = js_getstr(js, path_val, &path_len); 3548 if (!path) return js_mkerr(js, "Failed to get path string"); 3549 3550 char *path_cstr = strndup(path, path_len); 3551 if (!path_cstr) return js_mkerr(js, "Out of memory"); 3552 3553 uv_fs_t req; 3554 int result = uv_fs_readlink(NULL, &req, path_cstr, NULL); 3555 3556 if (result < 0 || !req.ptr) { 3557 ant_value_t err = fs_mk_uv_error(js, result, "readlink", path_cstr, NULL); 3558 uv_fs_req_cleanup(&req); 3559 free(path_cstr); 3560 return err; 3561 } 3562 3563 ant_value_t link = js_mkstr(js, (const char *)req.ptr, strlen((const char *)req.ptr)); 3564 uv_fs_req_cleanup(&req); 3565 free(path_cstr); 3566 return link; 3567} 3568 3569static ant_value_t builtin_fs_readlink(ant_t *js, ant_value_t *args, int nargs) { 3570 if (nargs < 1) return js_mkerr(js, "readlink() requires a path argument"); 3571 3572 ant_value_t path_val = fs_coerce_path(js, args[0]); 3573 if (vtype(path_val) != T_STR) return js_mkerr(js, "readlink() path must be a string"); 3574 3575 size_t path_len = 0; 3576 const char *path = js_getstr(js, path_val, &path_len); 3577 if (!path) return js_mkerr(js, "Failed to get path string"); 3578 3579 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3580 if (!req) return js_mkerr(js, "Out of memory"); 3581 3582 req->js = js; 3583 req->op_type = FS_OP_REALPATH; 3584 req->promise = js_mkpromise(js); 3585 req->path = strndup(path, path_len); 3586 req->uv_req.data = req; 3587 3588 if (!req->path) { 3589 free_fs_request(req); 3590 return js_mkerr(js, "Out of memory"); 3591 } 3592 3593 utarray_push_back(pending_requests, &req); 3594 int result = uv_fs_readlink(uv_default_loop(), &req->uv_req, req->path, on_realpath_complete); 3595 3596 if (result < 0) { 3597 fs_request_fail(req, result); 3598 req->completed = 1; 3599 complete_request(req); 3600 } 3601 3602 return req->promise; 3603} 3604 3605static ant_value_t builtin_fs_readdirSync(ant_t *js, ant_value_t *args, int nargs) { 3606 if (nargs < 1) return js_mkerr(js, "readdirSync() requires a path argument"); 3607 if (vtype(args[0]) != T_STR) return js_mkerr(js, "readdirSync() path must be a string"); 3608 3609 bool with_file_types = false; 3610 if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 3611 ant_value_t wft = js_get(js, args[1], "withFileTypes"); 3612 with_file_types = js_truthy(js, wft); 3613 } 3614 3615 size_t path_len; 3616 char *path = js_getstr(js, args[0], &path_len); 3617 if (!path) return js_mkerr(js, "Failed to get path string"); 3618 3619 char *path_cstr = strndup(path, path_len); 3620 if (!path_cstr) return js_mkerr(js, "Out of memory"); 3621 3622 uv_fs_t req; 3623 int result = uv_fs_scandir(NULL, &req, path_cstr, 0, NULL); 3624 3625 if (result < 0) { 3626 ant_value_t err = fs_mk_uv_error(js, result, "scandir", path_cstr, NULL); 3627 uv_fs_req_cleanup(&req); 3628 free(path_cstr); 3629 return err; 3630 } 3631 3632 free(path_cstr); 3633 3634 ant_value_t arr = js_mkarr(js); 3635 uv_dirent_t dirent; 3636 3637 while (uv_fs_scandir_next(&req, &dirent) != UV_EOF) { 3638 if (with_file_types) { 3639 ant_value_t entry = create_dirent_object(js, dirent.name, strlen(dirent.name), dirent.type); 3640 js_arr_push(js, arr, entry); 3641 } else { 3642 ant_value_t name = js_mkstr(js, dirent.name, strlen(dirent.name)); 3643 js_arr_push(js, arr, name); 3644 }} 3645 3646 uv_fs_req_cleanup(&req); 3647 return arr; 3648} 3649 3650static ant_value_t builtin_fs_readdir(ant_t *js, ant_value_t *args, int nargs) { 3651 if (nargs < 1) return js_mkerr(js, "readdir() requires a path argument"); 3652 if (vtype(args[0]) != T_STR) return js_mkerr(js, "readdir() path must be a string"); 3653 3654 bool with_file_types = false; 3655 if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 3656 ant_value_t wft = js_get(js, args[1], "withFileTypes"); 3657 with_file_types = js_truthy(js, wft); 3658 } 3659 3660 size_t path_len; 3661 char *path = js_getstr(js, args[0], &path_len); 3662 if (!path) return js_mkerr(js, "Failed to get path string"); 3663 3664 3665 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3666 if (!req) return js_mkerr(js, "Out of memory"); 3667 3668 req->js = js; 3669 req->op_type = FS_OP_READDIR; 3670 req->with_file_types = with_file_types; 3671 req->promise = js_mkpromise(js); 3672 req->path = strndup(path, path_len); 3673 req->uv_req.data = req; 3674 3675 utarray_push_back(pending_requests, &req); 3676 int result = uv_fs_scandir(uv_default_loop(), &req->uv_req, req->path, 0, on_readdir_complete); 3677 3678 if (result < 0) { 3679 fs_request_fail(req, result); 3680 req->completed = 1; 3681 complete_request(req); 3682 } 3683 3684 return req->promise; 3685} 3686 3687static void on_write_fd_complete(uv_fs_t *uv_req) { 3688 fs_request_t *req = (fs_request_t *)uv_req->data; 3689 3690 if (uv_req->result < 0) { 3691 fs_request_fail(req, (int)uv_req->result); 3692 req->completed = 1; 3693 complete_request(req); 3694 return; 3695 } 3696 3697 req->completed = 1; 3698 js_resolve_promise(req->js, req->promise, js_mknum((double)uv_req->result)); 3699 remove_pending_request(req); 3700 free_fs_request(req); 3701} 3702 3703static void on_read_fd_complete(uv_fs_t *uv_req) { 3704 fs_request_t *req = (fs_request_t *)uv_req->data; 3705 3706 if (uv_req->result < 0) { 3707 if (is_callable(req->callback_fn)) { 3708 ant_value_t err = js_mkerr(req->js, "read failed: %s", uv_strerror((int)uv_req->result)); 3709 ant_value_t cb_args[1] = { err }; 3710 fs_call_value(req->js, req->callback_fn, js_mkundef(), cb_args, 1); 3711 remove_pending_request(req); 3712 free_fs_request(req); 3713 return; 3714 } 3715 fs_request_fail(req, (int)uv_req->result); 3716 req->completed = 1; 3717 complete_request(req); 3718 return; 3719 } 3720 3721 ssize_t bytes_read = uv_req->result; 3722 3723 if (req->data && bytes_read > 0) { 3724 TypedArrayData *ta = buffer_get_typedarray_data(req->target_buffer); 3725 if (ta && ta->buffer && ta->buffer->data) { 3726 uint8_t *dest = ta->buffer->data + ta->byte_offset + req->buf_offset; 3727 memcpy(dest, req->data, (size_t)bytes_read); 3728 } 3729 } 3730 3731 if (is_callable(req->callback_fn)) { 3732 ant_value_t cb_args[3] = { js_mknull(), js_mknum((double)bytes_read), req->target_buffer }; 3733 fs_call_value(req->js, req->callback_fn, js_mkundef(), cb_args, 3); 3734 remove_pending_request(req); 3735 free_fs_request(req); 3736 return; 3737 } 3738 3739 req->completed = 1; 3740 js_resolve_promise(req->js, req->promise, js_mknum((double)bytes_read)); 3741 remove_pending_request(req); 3742 free_fs_request(req); 3743} 3744 3745static ant_value_t builtin_fs_read_fd(ant_t *js, ant_value_t *args, int nargs) { 3746 if (nargs < 2) return js_mkerr(js, "read() requires fd and buffer arguments"); 3747 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "read() fd must be a number"); 3748 3749 int fd = (int)js_getnum(args[0]); 3750 3751 ant_value_t buf_arg = args[1]; 3752 TypedArrayData *ta_data = buffer_get_typedarray_data(buf_arg); 3753 if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) 3754 return js_mkerr(js, "read() buffer argument must be a Buffer or TypedArray"); 3755 3756 size_t buf_len = ta_data->byte_length; 3757 size_t offset = 0; 3758 size_t length = buf_len; 3759 int64_t position = -1; 3760 3761 ant_value_t callback = js_mkundef(); 3762 int data_nargs = nargs; 3763 if (nargs >= 3 && is_callable(args[nargs - 1])) { 3764 callback = args[nargs - 1]; 3765 data_nargs = nargs - 1; 3766 } 3767 3768 if (data_nargs >= 3 && vtype(args[2]) == T_NUM) offset = (size_t)js_getnum(args[2]); 3769 if (data_nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); 3770 if (data_nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); 3771 3772 if (offset > buf_len) return js_mkerr(js, "read() offset out of bounds"); 3773 if (offset + length > buf_len) return js_mkerr(js, "read() length extends beyond buffer"); 3774 3775 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3776 if (!req) return js_mkerr(js, "Out of memory"); 3777 3778 req->js = js; 3779 req->op_type = FS_OP_READ_FD; 3780 req->promise = js_mkpromise(js); 3781 req->fd = fd; 3782 req->target_buffer = buf_arg; 3783 req->buf_offset = offset; 3784 req->data_len = length; 3785 req->callback_fn = callback; 3786 req->data = malloc(length > 0 ? length : 1); 3787 if (!req->data) { 3788 free(req); 3789 return js_mkerr(js, "Out of memory"); 3790 } 3791 req->uv_req.data = req; 3792 3793 utarray_push_back(pending_requests, &req); 3794 3795 uv_buf_t buf = uv_buf_init(req->data, (unsigned int)length); 3796 int result = uv_fs_read(uv_default_loop(), &req->uv_req, req->fd, &buf, 1, position, on_read_fd_complete); 3797 3798 if (result < 0) { 3799 fs_request_fail(req, result); 3800 req->completed = 1; 3801 complete_request(req); 3802 } 3803 3804 return req->promise; 3805} 3806 3807static ant_value_t builtin_fs_readSync(ant_t *js, ant_value_t *args, int nargs) { 3808 if (nargs < 2) return js_mkerr(js, "readSync() requires fd and buffer arguments"); 3809 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "readSync() fd must be a number"); 3810 3811 int fd = (int)js_getnum(args[0]); 3812 3813 TypedArrayData *ta_data = buffer_get_typedarray_data(args[1]); 3814 if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) 3815 return js_mkerr(js, "readSync() second argument must be a Buffer, TypedArray, or DataView"); 3816 3817 uint8_t *buf_data = ta_data->buffer->data + ta_data->byte_offset; 3818 size_t buf_len = ta_data->byte_length; 3819 size_t offset = 0; 3820 size_t length = buf_len; 3821 int64_t position = -1; 3822 3823 if (nargs >= 3) { 3824 if (vtype(args[2]) == T_OBJ) { 3825 ant_value_t off_val = js_get(js, args[2], "offset"); 3826 ant_value_t len_val = js_get(js, args[2], "length"); 3827 ant_value_t pos_val = js_get(js, args[2], "position"); 3828 if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); 3829 if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); 3830 else length = buf_len - offset; 3831 if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); 3832 } else if (vtype(args[2]) == T_NUM) { 3833 offset = (size_t)js_getnum(args[2]); 3834 length = buf_len - offset; 3835 if (nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); 3836 if (nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); 3837 }} 3838 3839 if (offset > buf_len) return js_mkerr(js, "offset is out of bounds"); 3840 if (offset + length > buf_len) return js_mkerr(js, "length extends beyond buffer"); 3841 3842 uv_fs_t req; 3843 uv_buf_t buf = uv_buf_init((char *)(buf_data + offset), (unsigned int)length); 3844 int result = uv_fs_read(uv_default_loop(), &req, fd, &buf, 1, position, NULL); 3845 uv_fs_req_cleanup(&req); 3846 3847 if (result < 0) return js_mkerr(js, "readSync failed: %s", uv_strerror(result)); 3848 return js_mknum((double)result); 3849} 3850 3851static ant_value_t builtin_fs_writeSync(ant_t *js, ant_value_t *args, int nargs) { 3852 if (nargs < 2) return js_mkerr(js, "writeSync() requires fd and data arguments"); 3853 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "writeSync() fd must be a number"); 3854 3855 int fd = (int)js_getnum(args[0]); 3856 3857 if (vtype(args[1]) == T_STR) { 3858 size_t str_len; 3859 const char *str = js_getstr(js, args[1], &str_len); 3860 if (!str) return js_mkerr(js, "Failed to get string"); 3861 3862 int64_t position = -1; 3863 if (nargs >= 3 && vtype(args[2]) == T_NUM) 3864 position = (int64_t)js_getnum(args[2]); 3865 3866 uv_fs_t req; 3867 uv_buf_t buf = uv_buf_init((char *)str, (unsigned int)str_len); 3868 int result = uv_fs_write(uv_default_loop(), &req, fd, &buf, 1, position, NULL); 3869 uv_fs_req_cleanup(&req); 3870 3871 if (result < 0) return js_mkerr(js, "writeSync failed: %s", uv_strerror(result)); 3872 return js_mknum((double)result); 3873 } 3874 3875 TypedArrayData *ta_data = buffer_get_typedarray_data(args[1]); 3876 if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) 3877 return js_mkerr(js, "writeSync() second argument must be a Buffer, TypedArray, DataView, or string"); 3878 3879 uint8_t *buf_data = ta_data->buffer->data + ta_data->byte_offset; 3880 size_t buf_len = ta_data->byte_length; 3881 size_t offset = 0; 3882 size_t length = buf_len; 3883 int64_t position = -1; 3884 3885 if (nargs >= 3) { 3886 if (vtype(args[2]) == T_OBJ) { 3887 ant_value_t off_val = js_get(js, args[2], "offset"); 3888 ant_value_t len_val = js_get(js, args[2], "length"); 3889 ant_value_t pos_val = js_get(js, args[2], "position"); 3890 if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); 3891 if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); 3892 else length = buf_len - offset; 3893 if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); 3894 } else if (vtype(args[2]) == T_NUM) { 3895 offset = (size_t)js_getnum(args[2]); 3896 length = buf_len - offset; 3897 if (nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); 3898 if (nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); 3899 } 3900 } 3901 3902 if (offset > buf_len) return js_mkerr(js, "offset is out of bounds"); 3903 if (offset + length > buf_len) return js_mkerr(js, "length extends beyond buffer"); 3904 3905 uv_fs_t req; 3906 uv_buf_t buf = uv_buf_init((char *)(buf_data + offset), (unsigned int)length); 3907 int result = uv_fs_write(uv_default_loop(), &req, fd, &buf, 1, position, NULL); 3908 uv_fs_req_cleanup(&req); 3909 3910 if (result < 0) return js_mkerr(js, "writeSync failed: %s", uv_strerror(result)); 3911 return js_mknum((double)result); 3912} 3913 3914static ant_value_t builtin_fs_write_fd(ant_t *js, ant_value_t *args, int nargs) { 3915 if (nargs < 2) return js_mkerr(js, "write() requires fd and data arguments"); 3916 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "write() fd must be a number"); 3917 3918 int fd = (int)js_getnum(args[0]); 3919 const char *write_data; 3920 size_t write_len; 3921 int64_t position = -1; 3922 3923 if (vtype(args[1]) == T_STR) { 3924 size_t str_len; 3925 const char *str = js_getstr(js, args[1], &str_len); 3926 if (!str) return js_mkerr(js, "Failed to get string"); 3927 3928 if (nargs >= 3 && vtype(args[2]) == T_NUM) 3929 position = (int64_t)js_getnum(args[2]); 3930 3931 write_data = str; 3932 write_len = str_len; 3933 } else { 3934 TypedArrayData *ta_data = buffer_get_typedarray_data(args[1]); 3935 if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) 3936 return js_mkerr(js, "write() second argument must be a Buffer, TypedArray, DataView, or string"); 3937 3938 uint8_t *buf_data = ta_data->buffer->data + ta_data->byte_offset; 3939 size_t buf_len = ta_data->byte_length; 3940 size_t offset = 0; 3941 size_t length = buf_len; 3942 3943 if (nargs >= 3) { 3944 if (vtype(args[2]) == T_OBJ) { 3945 ant_value_t off_val = js_get(js, args[2], "offset"); 3946 ant_value_t len_val = js_get(js, args[2], "length"); 3947 ant_value_t pos_val = js_get(js, args[2], "position"); 3948 if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); 3949 if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); 3950 else length = buf_len - offset; 3951 if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); 3952 } else if (vtype(args[2]) == T_NUM) { 3953 offset = (size_t)js_getnum(args[2]); 3954 length = buf_len - offset; 3955 if (nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); 3956 if (nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); 3957 }} 3958 3959 if (offset > buf_len) return js_mkerr(js, "offset is out of bounds"); 3960 if (offset + length > buf_len) return js_mkerr(js, "length extends beyond buffer"); 3961 3962 write_data = (const char *)(buf_data + offset); 3963 write_len = length; 3964 } 3965 3966 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 3967 if (!req) return js_mkerr(js, "Out of memory"); 3968 3969 req->js = js; 3970 req->op_type = FS_OP_WRITE_FD; 3971 req->promise = js_mkpromise(js); 3972 req->fd = fd; 3973 req->data = malloc(write_len); 3974 if (!req->data) { 3975 free(req); 3976 return js_mkerr(js, "Out of memory"); 3977 } 3978 memcpy(req->data, write_data, write_len); 3979 req->data_len = write_len; 3980 req->uv_req.data = req; 3981 3982 utarray_push_back(pending_requests, &req); 3983 3984 uv_buf_t buf = uv_buf_init(req->data, (unsigned int)req->data_len); 3985 int result = uv_fs_write(uv_default_loop(), &req->uv_req, req->fd, &buf, 1, position, on_write_fd_complete); 3986 3987 if (result < 0) { 3988 fs_request_fail(req, result); 3989 req->completed = 1; 3990 complete_request(req); 3991 } 3992 3993 return req->promise; 3994} 3995 3996static ant_value_t builtin_fs_writevSync(ant_t *js, ant_value_t *args, int nargs) { 3997 if (nargs < 2) return js_mkerr(js, "writevSync() requires fd and buffers arguments"); 3998 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "writevSync() fd must be a number"); 3999 4000 int fd = (int)js_getnum(args[0]); 4001 ant_offset_t arr_len = js_arr_len(js, args[1]); 4002 if (arr_len == 0) return js_mknum(0); 4003 4004 int64_t position = -1; 4005 if (nargs >= 3 && vtype(args[2]) == T_NUM) 4006 position = (int64_t)js_getnum(args[2]); 4007 4008 uv_buf_t *bufs = calloc((size_t)arr_len, sizeof(uv_buf_t)); 4009 if (!bufs) return js_mkerr(js, "Out of memory"); 4010 4011 for (ant_offset_t i = 0; i < arr_len; i++) { 4012 ant_value_t item = js_arr_get(js, args[1], i); 4013 TypedArrayData *ta = buffer_get_typedarray_data(item); 4014 if (!ta || !ta->buffer || !ta->buffer->data) { 4015 free(bufs); 4016 return js_mkerr(js, "writevSync() buffers must contain ArrayBufferViews"); 4017 } 4018 bufs[i] = uv_buf_init((char *)(ta->buffer->data + ta->byte_offset), (unsigned int)ta->byte_length); 4019 } 4020 4021 uv_fs_t req; 4022 int result = uv_fs_write(uv_default_loop(), &req, fd, bufs, (unsigned int)arr_len, position, NULL); 4023 uv_fs_req_cleanup(&req); 4024 free(bufs); 4025 4026 if (result < 0) return js_mkerr(js, "writevSync failed: %s", uv_strerror(result)); 4027 return js_mknum((double)result); 4028} 4029 4030static ant_value_t builtin_fs_writev_fd(ant_t *js, ant_value_t *args, int nargs) { 4031 if (nargs < 2) return js_mkerr(js, "writev() requires fd and buffers arguments"); 4032 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "writev() fd must be a number"); 4033 4034 int fd = (int)js_getnum(args[0]); 4035 ant_offset_t arr_len = js_arr_len(js, args[1]); 4036 4037 if (arr_len == 0) { 4038 ant_value_t promise = js_mkpromise(js); 4039 js_resolve_promise(js, promise, js_mknum(0)); 4040 return promise; 4041 } 4042 4043 int64_t position = -1; 4044 if (nargs >= 3 && vtype(args[2]) == T_NUM) 4045 position = (int64_t)js_getnum(args[2]); 4046 4047 size_t total_len = 0; 4048 for (ant_offset_t i = 0; i < arr_len; i++) { 4049 ant_value_t item = js_arr_get(js, args[1], i); 4050 TypedArrayData *ta = buffer_get_typedarray_data(item); 4051 if (!ta || !ta->buffer || !ta->buffer->data) 4052 return js_mkerr(js, "writev() buffers must contain ArrayBufferViews"); 4053 total_len += ta->byte_length; 4054 } 4055 4056 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 4057 if (!req) return js_mkerr(js, "Out of memory"); 4058 4059 req->data = malloc(total_len); 4060 if (!req->data) { 4061 free(req); 4062 return js_mkerr(js, "Out of memory"); 4063 } 4064 4065 size_t off = 0; 4066 for (ant_offset_t i = 0; i < arr_len; i++) { 4067 ant_value_t item = js_arr_get(js, args[1], i); 4068 TypedArrayData *ta = buffer_get_typedarray_data(item); 4069 memcpy(req->data + off, ta->buffer->data + ta->byte_offset, ta->byte_length); 4070 off += ta->byte_length; 4071 } 4072 4073 req->js = js; 4074 req->op_type = FS_OP_WRITE_FD; 4075 req->promise = js_mkpromise(js); 4076 req->fd = fd; 4077 req->data_len = total_len; 4078 req->uv_req.data = req; 4079 4080 utarray_push_back(pending_requests, &req); 4081 4082 uv_buf_t buf = uv_buf_init(req->data, (unsigned int)total_len); 4083 int result = uv_fs_write(uv_default_loop(), &req->uv_req, req->fd, &buf, 1, position, on_write_fd_complete); 4084 4085 if (result < 0) { 4086 fs_request_fail(req, result); 4087 req->completed = 1; 4088 complete_request(req); 4089 } 4090 4091 return req->promise; 4092} 4093 4094static int parse_open_flags(ant_t *js, ant_value_t arg) { 4095 if (vtype(arg) == T_NUM) return (int)js_getnum(arg); 4096 if (vtype(arg) != T_STR) return O_RDONLY; 4097 4098 size_t len; 4099 const char *str = js_getstr(js, arg, &len); 4100 if (!str) return O_RDONLY; 4101 4102 if (len == 1 && str[0] == 'r') return O_RDONLY; 4103 if (len == 2 && memcmp(str, "r+", 2) == 0) return O_RDWR; 4104 if (len == 1 && str[0] == 'w') return O_WRONLY | O_CREAT | O_TRUNC; 4105 if (len == 2 && memcmp(str, "wx", 2) == 0) return O_WRONLY | O_CREAT | O_TRUNC | O_EXCL; 4106 if (len == 2 && memcmp(str, "w+", 2) == 0) return O_RDWR | O_CREAT | O_TRUNC; 4107 if (len == 3 && memcmp(str, "wx+", 3) == 0) return O_RDWR | O_CREAT | O_TRUNC | O_EXCL; 4108 if (len == 1 && str[0] == 'a') return O_WRONLY | O_CREAT | O_APPEND; 4109 if (len == 2 && memcmp(str, "ax", 2) == 0) return O_WRONLY | O_CREAT | O_APPEND | O_EXCL; 4110 if (len == 2 && memcmp(str, "a+", 2) == 0) return O_RDWR | O_CREAT | O_APPEND; 4111 if (len == 3 && memcmp(str, "ax+", 3) == 0) return O_RDWR | O_CREAT | O_APPEND | O_EXCL; 4112 4113 return O_RDONLY; 4114} 4115 4116static ant_value_t builtin_fs_openSync(ant_t *js, ant_value_t *args, int nargs) { 4117 if (nargs < 1) return js_mkerr(js, "openSync() requires a path argument"); 4118 if (vtype(args[0]) != T_STR) return js_mkerr(js, "openSync() path must be a string"); 4119 4120 size_t path_len; 4121 char *path = js_getstr(js, args[0], &path_len); 4122 if (!path) return js_mkerr(js, "Failed to get path string"); 4123 4124 int flags = (nargs >= 2) ? parse_open_flags(js, args[1]) : O_RDONLY; 4125 int mode = (nargs >= 3 && vtype(args[2]) == T_NUM) ? (int)js_getnum(args[2]) : 0666; 4126 4127 char *path_cstr = strndup(path, path_len); 4128 if (!path_cstr) return js_mkerr(js, "Out of memory"); 4129 4130 uv_fs_t req; 4131 int result = uv_fs_open(uv_default_loop(), &req, path_cstr, flags, mode, NULL); 4132 4133 if (result < 0) { 4134 ant_value_t err = fs_mk_uv_error(js, result, "open", path_cstr, NULL); 4135 uv_fs_req_cleanup(&req); 4136 free(path_cstr); 4137 return err; 4138 } 4139 4140 uv_fs_req_cleanup(&req); 4141 free(path_cstr); 4142 4143 return js_mknum((double)result); 4144} 4145 4146static ant_value_t builtin_fs_closeSync(ant_t *js, ant_value_t *args, int nargs) { 4147 if (nargs < 1) return js_mkerr(js, "closeSync() requires a fd argument"); 4148 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "closeSync() fd must be a number"); 4149 4150 int fd = (int)js_getnum(args[0]); 4151 4152 uv_fs_t req; 4153 int result = uv_fs_close(uv_default_loop(), &req, fd, NULL); 4154 uv_fs_req_cleanup(&req); 4155 4156 if (result < 0) return js_mkerr(js, "closeSync failed: %s", uv_strerror(result)); 4157 return js_mkundef(); 4158} 4159 4160static void on_open_fd_complete(uv_fs_t *uv_req) { 4161 fs_request_t *req = (fs_request_t *)uv_req->data; 4162 4163 if (uv_req->result < 0) { 4164 fs_request_fail(req, (int)uv_req->result); 4165 req->completed = 1; 4166 complete_request(req); 4167 return; 4168 } 4169 4170 req->completed = 1; 4171 js_resolve_promise(req->js, req->promise, js_mknum((double)uv_req->result)); 4172 remove_pending_request(req); 4173 free_fs_request(req); 4174} 4175 4176static void on_open_filehandle_complete(uv_fs_t *uv_req) { 4177 fs_request_t *req = (fs_request_t *)uv_req->data; 4178 4179 if (uv_req->result < 0) { 4180 fs_request_fail(req, (int)uv_req->result); 4181 req->completed = 1; 4182 complete_request(req); 4183 return; 4184 } 4185 4186 req->completed = 1; 4187 js_resolve_promise( 4188 req->js, req->promise, 4189 fs_make_filehandle(req->js, (int)uv_req->result) 4190 ); 4191 4192 remove_pending_request(req); 4193 free_fs_request(req); 4194} 4195 4196static ant_value_t builtin_fs_open_fd(ant_t *js, ant_value_t *args, int nargs) { 4197 if (nargs < 1) return js_mkerr(js, "open() requires a path argument"); 4198 if (vtype(args[0]) != T_STR) return js_mkerr(js, "open() path must be a string"); 4199 4200 size_t path_len; 4201 char *path = js_getstr(js, args[0], &path_len); 4202 if (!path) return js_mkerr(js, "Failed to get path string"); 4203 4204 int flags = (nargs >= 2) ? parse_open_flags(js, args[1]) : O_RDONLY; 4205 int mode = (nargs >= 3 && vtype(args[2]) == T_NUM) ? (int)js_getnum(args[2]) : 0666; 4206 4207 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 4208 if (!req) return js_mkerr(js, "Out of memory"); 4209 4210 req->js = js; 4211 req->op_type = FS_OP_OPEN; 4212 req->promise = js_mkpromise(js); 4213 req->path = strndup(path, path_len); 4214 req->uv_req.data = req; 4215 4216 utarray_push_back(pending_requests, &req); 4217 int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, flags, mode, on_open_fd_complete); 4218 4219 if (result < 0) { 4220 fs_request_fail(req, result); 4221 req->completed = 1; 4222 complete_request(req); 4223 } 4224 4225 return req->promise; 4226} 4227 4228static ant_value_t builtin_fs_open_filehandle(ant_t *js, ant_value_t *args, int nargs) { 4229 if (nargs < 1) return js_mkerr(js, "open() requires a path argument"); 4230 if (vtype(args[0]) != T_STR) return js_mkerr(js, "open() path must be a string"); 4231 4232 size_t path_len; 4233 char *path = js_getstr(js, args[0], &path_len); 4234 if (!path) return js_mkerr(js, "Failed to get path string"); 4235 4236 int flags = (nargs >= 2) ? parse_open_flags(js, args[1]) : O_RDONLY; 4237 int mode = (nargs >= 3 && vtype(args[2]) == T_NUM) ? (int)js_getnum(args[2]) : 0666; 4238 4239 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 4240 if (!req) return js_mkerr(js, "Out of memory"); 4241 4242 req->js = js; 4243 req->op_type = FS_OP_OPEN; 4244 req->promise = js_mkpromise(js); 4245 req->path = strndup(path, path_len); 4246 req->uv_req.data = req; 4247 4248 utarray_push_back(pending_requests, &req); 4249 int result = uv_fs_open( 4250 uv_default_loop(), &req->uv_req, req->path, 4251 flags, mode, on_open_filehandle_complete 4252 ); 4253 4254 if (result < 0) { 4255 fs_request_fail(req, result); 4256 req->completed = 1; 4257 complete_request(req); 4258 } 4259 4260 return req->promise; 4261} 4262 4263static void on_close_fd_complete(uv_fs_t *uv_req) { 4264 fs_request_t *req = (fs_request_t *)uv_req->data; 4265 4266 if (uv_req->result < 0) { 4267 fs_request_fail(req, (int)uv_req->result); 4268 req->completed = 1; 4269 complete_request(req); 4270 return; 4271 } 4272 4273 req->completed = 1; 4274 js_resolve_promise(req->js, req->promise, js_mkundef()); 4275 remove_pending_request(req); 4276 free_fs_request(req); 4277} 4278 4279static ant_value_t builtin_fs_close_fd(ant_t *js, ant_value_t *args, int nargs) { 4280 if (nargs < 1) return js_mkerr(js, "close() requires a fd argument"); 4281 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "close() fd must be a number"); 4282 4283 int fd = (int)js_getnum(args[0]); 4284 4285 fs_request_t *req = calloc(1, sizeof(fs_request_t)); 4286 if (!req) return js_mkerr(js, "Out of memory"); 4287 4288 req->js = js; 4289 req->op_type = FS_OP_CLOSE; 4290 req->promise = js_mkpromise(js); 4291 req->fd = fd; 4292 req->uv_req.data = req; 4293 4294 utarray_push_back(pending_requests, &req); 4295 int result = uv_fs_close(uv_default_loop(), &req->uv_req, req->fd, on_close_fd_complete); 4296 4297 if (result < 0) { 4298 fs_request_fail(req, result); 4299 req->completed = 1; 4300 complete_request(req); 4301 } 4302 4303 return req->promise; 4304} 4305 4306static ant_value_t builtin_fs_watch(ant_t *js, ant_value_t *args, int nargs) { 4307 ant_value_t path_val = 0; 4308 ant_value_t watcher_obj = 0; 4309 fs_watch_options_t opts; 4310 fs_watcher_t *watcher = NULL; 4311 uv_handle_t *handle = NULL; 4312 4313 const char *path = NULL; 4314 size_t path_len = 0; 4315 int rc = 0; 4316 4317 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "watch() requires a path argument"); 4318 if (!fs_parse_watch_options(js, args, nargs, &opts)) 4319 return js_mkerr_typed(js, JS_ERR_TYPE, "watch() options must be a string, object, or callback"); 4320 4321 path_val = fs_coerce_path(js, args[0]); 4322 if (vtype(path_val) != T_STR) return js_mkerr_typed(js, JS_ERR_TYPE, "watch() path must be a string or URL"); 4323 4324 path = js_getstr(js, path_val, &path_len); 4325 if (!path || path_len == 0) return js_mkerr(js, "watch() path must not be empty"); 4326 4327 watcher = fs_watcher_new(js, FS_WATCH_MODE_EVENT); 4328 if (!watcher) return js_mkerr(js, "Out of memory"); 4329 4330 watcher_obj = fs_watcher_make_object(js, watcher); 4331 if (is_err(watcher_obj)) { 4332 fs_watcher_free(watcher); 4333 return watcher_obj; 4334 } 4335 4336 rc = fs_watcher_start_event(watcher, path, opts.persistent, opts.recursive); 4337 if (rc != 0) { 4338 js_set_native_ptr(watcher_obj, NULL); 4339 fs_watcher_free(watcher); 4340 return fs_watch_error(js, rc, path); 4341 } 4342 4343 fs_add_active_watcher(watcher); 4344 if (!opts.persistent) { 4345 handle = fs_watcher_uv_handle(watcher); 4346 if (handle) uv_unref(handle); 4347 } 4348 if (is_callable(opts.listener)) 4349 eventemitter_add_listener(js, watcher_obj, "change", opts.listener, false); 4350 return watcher_obj; 4351} 4352 4353static ant_value_t builtin_fs_watchFile(ant_t *js, ant_value_t *args, int nargs) { 4354 ant_value_t path_val = 0; 4355 fs_watchfile_options_t opts; 4356 4357 fs_watcher_t *watcher = NULL; 4358 uv_handle_t *handle = NULL; 4359 4360 const char *path = NULL; 4361 size_t path_len = 0; 4362 int rc = 0; 4363 4364 if (nargs < 2) 4365 return js_mkerr_typed(js, JS_ERR_TYPE, "watchFile() requires a path and listener"); 4366 if (!fs_parse_watchfile_options(js, args, nargs, &opts)) 4367 return js_mkerr_typed(js, JS_ERR_TYPE, "watchFile() requires a listener callback"); 4368 4369 path_val = fs_coerce_path(js, args[0]); 4370 if (vtype(path_val) != T_STR) return js_mkerr_typed(js, JS_ERR_TYPE, "watchFile() path must be a string or URL"); 4371 4372 path = js_getstr(js, path_val, &path_len); 4373 if (!path || path_len == 0) return js_mkerr(js, "watchFile() path must not be empty"); 4374 4375 watcher = fs_watcher_new(js, FS_WATCH_MODE_STAT); 4376 if (!watcher) return js_mkerr(js, "Out of memory"); 4377 4378 watcher->callback = opts.listener; 4379 watcher->last_stat_valid = fs_stat_path_sync(path, &watcher->last_stat); 4380 4381 rc = fs_watcher_start_poll(watcher, path, opts.persistent, opts.interval_ms); 4382 if (rc != 0) { 4383 fs_watcher_free(watcher); 4384 return fs_watch_error(js, rc, path); 4385 } 4386 4387 fs_add_active_watcher(watcher); 4388 if (!opts.persistent) { 4389 handle = fs_watcher_uv_handle(watcher); 4390 if (handle) uv_unref(handle); 4391 } 4392 return js_mkundef(); 4393} 4394 4395static ant_value_t builtin_fs_unwatchFile(ant_t *js, ant_value_t *args, int nargs) { 4396 ant_value_t path_val = 0; 4397 ant_value_t listener = js_mkundef(); 4398 4399 char *resolved = NULL; 4400 fs_watcher_t *watcher = NULL; 4401 fs_watcher_t *next = NULL; 4402 4403 const char *path = NULL; 4404 size_t path_len = 0; 4405 4406 if (nargs < 1) return js_mkundef(); 4407 4408 path_val = fs_coerce_path(js, args[0]); 4409 if (vtype(path_val) != T_STR) return js_mkundef(); 4410 4411 path = js_getstr(js, path_val, &path_len); 4412 if (!path || path_len == 0) return js_mkundef(); 4413 if (nargs > 1 && is_callable(args[1])) listener = args[1]; 4414 4415 resolved = ant_watch_resolve_path(path); 4416 if (!resolved) return js_mkundef(); 4417 4418 for (watcher = active_watchers; watcher; watcher = next) { 4419 next = watcher->next_active; 4420 if (watcher->mode != FS_WATCH_MODE_STAT) continue; 4421 if (!watcher->path || strcmp(watcher->path, resolved) != 0) continue; 4422 if (is_callable(listener) && watcher->callback != listener) continue; 4423 fs_watcher_close_native(watcher); 4424 } 4425 4426 free(resolved); 4427 return js_mkundef(); 4428} 4429 4430void init_fs_module(void) { 4431 utarray_new(pending_requests, &ut_ptr_icd); 4432 4433 ant_t *js = rt->js; 4434 ant_value_t glob = js->global; 4435 4436 ant_value_t stats_ctor = js_mkobj(js); 4437 ant_value_t stats_proto = js_mkobj(js); 4438 4439 js_set(js, stats_proto, "isFile", js_mkfun(stat_isFile)); 4440 js_set(js, stats_proto, "isDirectory", js_mkfun(stat_isDirectory)); 4441 js_set(js, stats_proto, "isSymbolicLink", js_mkfun(stat_isSymbolicLink)); 4442 js_set_sym(js, stats_proto, get_toStringTag_sym(), js_mkstr(js, "Stats", 5)); 4443 4444 js_mkprop_fast(js, stats_ctor, "prototype", 9, stats_proto); 4445 js_mkprop_fast(js, stats_ctor, "name", 4, js_mkstr(js, "Stats", 5)); 4446 js_set_descriptor(js, stats_ctor, "name", 4, 0); 4447 4448 js_set(js, glob, "Stats", js_obj_to_func(stats_ctor)); 4449 4450 g_dirent_proto = js_mkobj(js); 4451 js_set(js, g_dirent_proto, "isFile", js_mkfun(dirent_isFile)); 4452 js_set(js, g_dirent_proto, "isDirectory", js_mkfun(dirent_isDirectory)); 4453 js_set(js, g_dirent_proto, "isSymbolicLink", js_mkfun(dirent_isSymbolicLink)); 4454 js_set(js, g_dirent_proto, "isBlockDevice", js_mkfun(dirent_isBlockDevice)); 4455 js_set(js, g_dirent_proto, "isCharacterDevice", js_mkfun(dirent_isCharacterDevice)); 4456 js_set(js, g_dirent_proto, "isFIFO", js_mkfun(dirent_isFIFO)); 4457 js_set(js, g_dirent_proto, "isSocket", js_mkfun(dirent_isSocket)); 4458 js_set_sym(js, g_dirent_proto, get_toStringTag_sym(), js_mkstr(js, "Dirent", 6)); 4459 gc_register_root(&g_dirent_proto); 4460} 4461 4462static ant_value_t fs_callback_success_handler(ant_t *js, ant_value_t *args, int nargs) { 4463 ant_value_t ctx = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 4464 ant_value_t callback = js_get_slot(ctx, SLOT_DATA); 4465 4466 if (!is_callable(callback)) return js_mkundef(); 4467 4468 if (js_truthy(js, js_get(js, ctx, "existsStyle"))) { 4469 ant_value_t cb_args[1] = { nargs > 0 ? args[0] : js_false }; 4470 fs_call_value(js, callback, js_mkundef(), cb_args, 1); 4471 return js_mkundef(); 4472 } 4473 4474 ant_value_t cb_args[2] = { js_mknull(), nargs > 0 ? args[0] : js_mkundef() }; 4475 fs_call_value(js, callback, js_mkundef(), cb_args, 2); 4476 return js_mkundef(); 4477} 4478 4479static ant_value_t fs_callback_error_handler(ant_t *js, ant_value_t *args, int nargs) { 4480 ant_value_t ctx = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 4481 ant_value_t callback = js_get_slot(ctx, SLOT_DATA); 4482 ant_value_t cb_args[1]; 4483 4484 if (!is_callable(callback)) return js_mkundef(); 4485 4486 if (js_truthy(js, js_get(js, ctx, "existsStyle"))) { 4487 cb_args[0] = js_false; 4488 fs_call_value(js, callback, js_mkundef(), cb_args, 1); 4489 return js_mkundef(); 4490 } 4491 4492 cb_args[0] = nargs > 0 ? args[0] : js_mkundef(); 4493 fs_call_value(js, callback, js_mkundef(), cb_args, 1); 4494 return js_mkundef(); 4495} 4496 4497static void fs_callback_emit_success( 4498 ant_t *js, 4499 ant_value_t callback, 4500 bool exists_style, 4501 ant_value_t value 4502) { 4503 if (exists_style) { 4504 ant_value_t cb_args[1] = { value }; 4505 fs_call_value(js, callback, js_mkundef(), cb_args, 1); 4506 return; 4507 } 4508 4509 ant_value_t cb_args[2] = { js_mknull(), value }; 4510 fs_call_value(js, callback, js_mkundef(), cb_args, 2); 4511} 4512 4513static void fs_callback_emit_error( 4514 ant_t *js, 4515 ant_value_t callback, 4516 bool exists_style, 4517 ant_value_t error 4518) { 4519 ant_value_t cb_args[1]; 4520 4521 cb_args[0] = exists_style ? js_false : error; 4522 fs_call_value(js, callback, js_mkundef(), cb_args, 1); 4523} 4524 4525static ant_value_t fs_callback_attach_promise( 4526 ant_t *js, 4527 ant_value_t promise, 4528 ant_value_t callback, 4529 bool exists_style 4530) { 4531 GC_ROOT_SAVE(root_mark, js); 4532 ant_value_t success_fn = js_mkundef(); 4533 ant_value_t error_fn = js_mkundef(); 4534 4535 GC_ROOT_PIN(js, promise); 4536 GC_ROOT_PIN(js, callback); 4537 4538 ant_value_t success_ctx = js_mkobj(js); 4539 GC_ROOT_PIN(js, success_ctx); 4540 ant_value_t error_ctx = js_mkobj(js); 4541 GC_ROOT_PIN(js, error_ctx); 4542 4543 js_set_slot(success_ctx, SLOT_DATA, callback); 4544 js_set_slot(error_ctx, SLOT_DATA, callback); 4545 if (exists_style) { 4546 js_set(js, success_ctx, "existsStyle", js_true); 4547 js_set(js, error_ctx, "existsStyle", js_true); 4548 } 4549 4550 success_fn = js_heavy_mkfun(js, fs_callback_success_handler, success_ctx); 4551 GC_ROOT_PIN(js, success_fn); 4552 error_fn = js_heavy_mkfun(js, fs_callback_error_handler, error_ctx); 4553 GC_ROOT_PIN(js, error_fn); 4554 4555 js_promise_then(js, promise, success_fn, error_fn); 4556 GC_ROOT_RESTORE(js, root_mark); 4557 return js_mkundef(); 4558} 4559 4560static ant_value_t fs_callback_wrapper_call(ant_t *js, ant_value_t *args, int nargs) { 4561 GC_ROOT_SAVE(root_mark, js); 4562 ant_value_t wrapper = js_getcurrentfunc(js); 4563 ant_value_t config = js_get_slot(wrapper, SLOT_DATA); 4564 ant_value_t original = js_get_slot(config, SLOT_DATA); 4565 4566 ant_value_t callback = js_mkundef(); 4567 ant_value_t result = js_mkundef(); 4568 ant_value_t this_arg = js_getthis(js); 4569 ant_value_t ex = js_mkundef(); 4570 4571 bool exists_style = false; 4572 int call_nargs = nargs; 4573 4574 GC_ROOT_PIN(js, original); 4575 GC_ROOT_PIN(js, this_arg); 4576 4577 exists_style = js_truthy(js, js_get(js, config, "existsStyle")); 4578 if (nargs > 0 && is_callable(args[nargs - 1])) { 4579 callback = args[nargs - 1]; 4580 GC_ROOT_PIN(js, callback); 4581 call_nargs--; 4582 } 4583 4584 result = fs_call_value(js, original, this_arg, args, call_nargs); 4585 GC_ROOT_PIN(js, result); 4586 4587 if (!is_callable(callback)) { 4588 GC_ROOT_RESTORE(js, root_mark); 4589 return result; 4590 } 4591 4592 if (is_err(result) || js->thrown_exists) { 4593 ex = js->thrown_exists ? js->thrown_value : result; 4594 GC_ROOT_PIN(js, ex); 4595 js->thrown_exists = false; 4596 js->thrown_value = js_mkundef(); 4597 js->thrown_stack = js_mkundef(); 4598 fs_callback_emit_error(js, callback, exists_style, ex); 4599 GC_ROOT_RESTORE(js, root_mark); 4600 return js_mkundef(); 4601 } 4602 4603 if (vtype(result) != T_PROMISE) { 4604 fs_callback_emit_success(js, callback, exists_style, result); 4605 GC_ROOT_RESTORE(js, root_mark); 4606 return js_mkundef(); 4607 } 4608 4609 ant_value_t attach_result = fs_callback_attach_promise(js, result, callback, exists_style); 4610 GC_ROOT_RESTORE(js, root_mark); 4611 return attach_result; 4612} 4613 4614static ant_value_t fs_make_callback_wrapper(ant_t *js, ant_value_t original, bool exists_style) { 4615 ant_value_t config = js_mkobj(js); 4616 4617 js_set_slot(config, SLOT_DATA, original); 4618 if (exists_style) js_set(js, config, "existsStyle", js_true); 4619 return js_heavy_mkfun(js, fs_callback_wrapper_call, config); 4620} 4621 4622static void fs_set_promise_methods(ant_t *js, ant_value_t lib) { 4623 js_set(js, lib, "appendFile", js_mkfun(builtin_fs_appendFile)); 4624 js_set(js, lib, "cp", js_mkfun(builtin_fs_cp)); 4625 js_set(js, lib, "copyFile", js_mkfun(builtin_fs_copyFile)); 4626 js_set(js, lib, "readFile", js_mkfun(builtin_fs_readFile)); 4627 js_set(js, lib, "open", js_mkfun(builtin_fs_open_filehandle)); 4628 js_set(js, lib, "close", js_mkfun(builtin_fs_close_fd)); 4629 js_set(js, lib, "writeFile", js_mkfun(builtin_fs_writeFile)); 4630 js_set(js, lib, "write", js_mkfun(builtin_fs_write_fd)); 4631 js_set(js, lib, "writev", js_mkfun(builtin_fs_writev_fd)); 4632 js_set(js, lib, "rename", js_mkfun(builtin_fs_rename)); 4633 js_set(js, lib, "rm", js_mkfun(builtin_fs_rm)); 4634 js_set(js, lib, "unlink", js_mkfun(builtin_fs_unlink)); 4635 js_set(js, lib, "mkdir", js_mkfun(builtin_fs_mkdir)); 4636 js_set(js, lib, "mkdtemp", js_mkfun(builtin_fs_mkdtemp)); 4637 js_set(js, lib, "rmdir", js_mkfun(builtin_fs_rmdir)); 4638 js_set(js, lib, "stat", js_mkfun(builtin_fs_stat)); 4639 js_set(js, lib, "lstat", js_mkfun(builtin_fs_lstat)); 4640 js_set(js, lib, "fstat", js_mkfun(builtin_fs_fstat)); 4641 js_set(js, lib, "utimes", js_mkfun(builtin_fs_utimes)); 4642 js_set(js, lib, "futimes", js_mkfun(builtin_fs_futimes)); 4643 js_set(js, lib, "exists", js_mkfun(builtin_fs_exists)); 4644 js_set(js, lib, "access", js_mkfun(builtin_fs_access)); 4645 js_set(js, lib, "chmod", js_mkfun(builtin_fs_chmod)); 4646 js_set(js, lib, "readdir", js_mkfun(builtin_fs_readdir)); 4647 js_set(js, lib, "realpath", js_mkfun(builtin_fs_realpath)); 4648 js_set(js, lib, "readlink", js_mkfun(builtin_fs_readlink)); 4649} 4650 4651static void fs_set_callback_compatible_methods(ant_t *js, ant_value_t lib) { 4652 ant_value_t realpath = fs_make_callback_wrapper(js, js_mkfun(builtin_fs_realpath), false); 4653 4654 js_set(js, lib, "appendFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_appendFile), false)); 4655 js_set(js, lib, "cp", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_cp), false)); 4656 js_set(js, lib, "copyFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_copyFile), false)); 4657 js_set(js, lib, "readFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_readFile), false)); 4658 js_set(js, lib, "open", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_open_fd), false)); 4659 js_set(js, lib, "close", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_close_fd), false)); 4660 js_set(js, lib, "writeFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_writeFile), false)); 4661 js_set(js, lib, "write", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_write_fd), false)); 4662 js_set(js, lib, "writev", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_writev_fd), false)); 4663 js_set(js, lib, "rename", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rename), false)); 4664 js_set(js, lib, "rm", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rm), false)); 4665 js_set(js, lib, "unlink", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_unlink), false)); 4666 js_set(js, lib, "mkdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_mkdir), false)); 4667 js_set(js, lib, "mkdtemp", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_mkdtemp), false)); 4668 js_set(js, lib, "rmdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rmdir), false)); 4669 js_set(js, lib, "stat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_stat), false)); 4670 js_set(js, lib, "lstat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_lstat), false)); 4671 js_set(js, lib, "fstat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_fstat), false)); 4672 js_set(js, lib, "utimes", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_utimes), false)); 4673 js_set(js, lib, "futimes", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_futimes), false)); 4674 js_set(js, lib, "exists", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_exists), true)); 4675 js_set(js, lib, "access", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_access), false)); 4676 js_set(js, lib, "chmod", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_chmod), false)); 4677 js_set(js, lib, "readdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_readdir), false)); 4678 js_set(js, lib, "readlink", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_readlink), false)); 4679 4680 js_set(js, realpath, "native", realpath); 4681 js_set(js, lib, "realpath", realpath); 4682} 4683 4684static ant_value_t fs_make_constants(ant_t *js) { 4685 ant_value_t constants = js_newobj(js); 4686 js_set(js, constants, "F_OK", js_mknum(F_OK)); 4687 js_set(js, constants, "R_OK", js_mknum(R_OK)); 4688 js_set(js, constants, "W_OK", js_mknum(W_OK)); 4689 js_set(js, constants, "X_OK", js_mknum(X_OK)); 4690 js_set(js, constants, "O_RDONLY", js_mknum(O_RDONLY)); 4691 js_set(js, constants, "O_WRONLY", js_mknum(O_WRONLY)); 4692 js_set(js, constants, "O_RDWR", js_mknum(O_RDWR)); 4693 js_set(js, constants, "O_CREAT", js_mknum(O_CREAT)); 4694 js_set(js, constants, "O_EXCL", js_mknum(O_EXCL)); 4695 js_set(js, constants, "O_TRUNC", js_mknum(O_TRUNC)); 4696 js_set(js, constants, "O_APPEND", js_mknum(O_APPEND)); 4697#ifdef O_SYMLINK 4698 js_set(js, constants, "O_SYMLINK", js_mknum(O_SYMLINK)); 4699#endif 4700#ifdef O_NOFOLLOW 4701 js_set(js, constants, "O_NOFOLLOW", js_mknum(O_NOFOLLOW)); 4702#endif 4703#ifdef S_IFMT 4704 js_set(js, constants, "S_IFMT", js_mknum(S_IFMT)); 4705#endif 4706#ifdef S_IFREG 4707 js_set(js, constants, "S_IFREG", js_mknum(S_IFREG)); 4708#endif 4709#ifdef S_IFDIR 4710 js_set(js, constants, "S_IFDIR", js_mknum(S_IFDIR)); 4711#endif 4712#ifdef S_IFLNK 4713 js_set(js, constants, "S_IFLNK", js_mknum(S_IFLNK)); 4714#endif 4715 return constants; 4716} 4717 4718static ant_value_t builtin_fs_promises_getter(ant_t *js, ant_value_t *args, int nargs) { 4719 ant_value_t getter_fn = js_getcurrentfunc(js); 4720 ant_value_t cached = js_get_slot(getter_fn, SLOT_DATA); 4721 if (is_object_type(cached)) return cached; 4722 4723 ant_value_t promises = fs_promises_library(js); 4724 js_set_slot(getter_fn, SLOT_DATA, promises); 4725 return promises; 4726} 4727 4728ant_value_t fs_library(ant_t *js) { 4729 ant_value_t lib = js_mkobj(js); 4730 ant_value_t realpath_sync = js_heavy_mkfun(js, builtin_fs_realpathSync, js_mkundef()); 4731 4732 fs_set_callback_compatible_methods(js, lib); 4733 fs_init_watch_constructors(js); 4734 fs_init_stream_constructors(js); 4735 4736 js_set(js, lib, "read", js_mkfun(builtin_fs_read_fd)); 4737 js_set(js, lib, "readFileSync", js_mkfun(builtin_fs_readFileSync)); 4738 js_set(js, lib, "readSync", js_mkfun(builtin_fs_readSync)); 4739 js_set(js, lib, "stream", js_mkfun(builtin_fs_readBytes)); 4740 js_set(js, lib, "createReadStream", js_mkfun(builtin_fs_createReadStream)); 4741 js_set(js, lib, "createWriteStream", js_mkfun(builtin_fs_createWriteStream)); 4742 js_set(js, lib, "openSync", js_mkfun(builtin_fs_openSync)); 4743 js_set(js, lib, "closeSync", js_mkfun(builtin_fs_closeSync)); 4744 js_set(js, lib, "writeFileSync", js_mkfun(builtin_fs_writeFileSync)); 4745 js_set(js, lib, "writeSync", js_mkfun(builtin_fs_writeSync)); 4746 js_set(js, lib, "writevSync", js_mkfun(builtin_fs_writevSync)); 4747 js_set(js, lib, "appendFileSync", js_mkfun(builtin_fs_appendFileSync)); 4748 js_set(js, lib, "cpSync", js_mkfun(builtin_fs_cpSync)); 4749 js_set(js, lib, "copyFileSync", js_mkfun(builtin_fs_copyFileSync)); 4750 js_set(js, lib, "renameSync", js_mkfun(builtin_fs_renameSync)); 4751 js_set(js, lib, "rmSync", js_mkfun(builtin_fs_rmSync)); 4752 js_set(js, lib, "unlinkSync", js_mkfun(builtin_fs_unlinkSync)); 4753 js_set(js, lib, "mkdirSync", js_mkfun(builtin_fs_mkdirSync)); 4754 js_set(js, lib, "mkdtempSync", js_mkfun(builtin_fs_mkdtempSync)); 4755 js_set(js, lib, "rmdirSync", js_mkfun(builtin_fs_rmdirSync)); 4756 js_set(js, lib, "statSync", js_mkfun(builtin_fs_statSync)); 4757 js_set(js, lib, "lstatSync", js_mkfun(builtin_fs_lstatSync)); 4758 js_set(js, lib, "fstatSync", js_mkfun(builtin_fs_fstatSync)); 4759 js_set(js, lib, "utimesSync", js_mkfun(builtin_fs_utimesSync)); 4760 js_set(js, lib, "futimesSync", js_mkfun(builtin_fs_futimesSync)); 4761 js_set(js, lib, "existsSync", js_mkfun(builtin_fs_existsSync)); 4762 js_set(js, lib, "accessSync", js_mkfun(builtin_fs_accessSync)); 4763 js_set(js, lib, "chmodSync", js_mkfun(builtin_fs_chmodSync)); 4764 js_set(js, lib, "readdirSync", js_mkfun(builtin_fs_readdirSync)); 4765 js_set(js, lib, "realpathSync", realpath_sync); 4766 js_set(js, lib, "readlinkSync", js_mkfun(builtin_fs_readlinkSync)); 4767 js_set(js, lib, "watch", js_mkfun(builtin_fs_watch)); 4768 js_set(js, lib, "watchFile", js_mkfun(builtin_fs_watchFile)); 4769 js_set(js, lib, "unwatchFile", js_mkfun(builtin_fs_unwatchFile)); 4770 js_set(js, lib, "FSWatcher", g_fswatcher_ctor); 4771 js_set(js, lib, "ReadStream", g_readstream_ctor); 4772 js_set(js, lib, "WriteStream", g_writestream_ctor); 4773 js_set(js, realpath_sync, "native", realpath_sync); 4774 4775 js_set_getter_desc( 4776 js, lib, 4777 "promises", 8, 4778 js_heavy_mkfun(js, builtin_fs_promises_getter, js_mkundef()), 4779 JS_DESC_E | JS_DESC_C 4780 ); 4781 4782 js_set(js, lib, "constants", fs_make_constants(js)); 4783 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "fs", 2)); 4784 4785 return lib; 4786} 4787 4788ant_value_t fs_promises_library(ant_t *js) { 4789 ant_value_t lib = js_mkobj(js); 4790 4791 fs_set_promise_methods(js, lib); 4792 js_set(js, lib, "constants", fs_make_constants(js)); 4793 4794 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "fs/promises", 11)); 4795 return lib; 4796} 4797 4798ant_value_t fs_constants_library(ant_t *js) { 4799 ant_value_t constants = fs_make_constants(js); 4800 js_set(js, constants, "default", constants); 4801 js_set_slot_wb(js, constants, SLOT_DEFAULT, constants); 4802 js_set_sym(js, constants, get_toStringTag_sym(), js_mkstr(js, "constants", 9)); 4803 return constants; 4804} 4805 4806int has_pending_fs_ops(void) { 4807 return pending_requests && utarray_len(pending_requests) > 0; 4808} 4809 4810void gc_mark_fs(ant_t *js, gc_mark_fn mark) { 4811 fs_watcher_t *watcher = NULL; 4812 4813 if (g_fswatcher_proto) mark(js, g_fswatcher_proto); 4814 if (g_fswatcher_ctor) mark(js, g_fswatcher_ctor); 4815 if (g_filehandle_proto) mark(js, g_filehandle_proto); 4816 if (g_readstream_proto) mark(js, g_readstream_proto); 4817 if (g_readstream_ctor) mark(js, g_readstream_ctor); 4818 if (g_writestream_proto) mark(js, g_writestream_proto); 4819 if (g_writestream_ctor) mark(js, g_writestream_ctor); 4820 if (!pending_requests) return; 4821 4822 unsigned int len = utarray_len(pending_requests); 4823 for (unsigned int i = 0; i < len; i++) { 4824 fs_request_t **reqp = (fs_request_t **)utarray_eltptr(pending_requests, i); 4825 if (reqp && *reqp) { 4826 mark(js, (*reqp)->promise); 4827 if (is_object_type((*reqp)->target_buffer)) mark(js, (*reqp)->target_buffer); 4828 if (is_callable((*reqp)->callback_fn)) mark(js, (*reqp)->callback_fn); 4829 }} 4830 4831 for (watcher = active_watchers; watcher; watcher = watcher->next_active) { 4832 if (vtype(watcher->obj) == T_OBJ) mark(js, watcher->obj); 4833 if (vtype(watcher->callback) != T_UNDEF) mark(js, watcher->callback); 4834 } 4835}