1 /* 2 * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /** 18 * # Filesystem Access 19 * 20 * The `fs` module provides functions for interacting with the file system. 21 * 22 * Functions can be individually imported and directly accessed using the 23 * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import} 24 * syntax: 25 * 26 * ``` 27 * import { readlink, popen } from 'fs'; 28 * 29 * let dest = readlink('/sys/class/net/eth0'); 30 * let proc = popen('ps ww'); 31 * ``` 32 * 33 * Alternatively, the module namespace can be imported 34 * using a wildcard import statement: 35 * 36 * ``` 37 * import * as fs from 'fs'; 38 * 39 * let dest = fs.readlink('/sys/class/net/eth0'); 40 * let proc = fs.popen('ps ww'); 41 * ``` 42 * 43 * Additionally, the filesystem module namespace may also be imported by invoking 44 * the `ucode` interpreter with the `-lfs` switch. 45 * 46 * @module fs 47 */ 48 49 #include <stdio.h> 50 #include <errno.h> 51 #include <string.h> 52 #include <dirent.h> 53 #include <unistd.h> 54 #include <sys/stat.h> 55 #include <sys/types.h> 56 #include <grp.h> 57 #include <pwd.h> 58 #include <glob.h> 59 #include <fnmatch.h> 60 #include <limits.h> 61 #include <fcntl.h> 62 63 #include "ucode/module.h" 64 #include "ucode/platform.h" 65 66 #define err_return(err) do { last_error = err; return NULL; } while(0) 67 68 //static const uc_ops *ops; 69 static uc_resource_type_t *file_type, *proc_type, *dir_type; 70 71 static int last_error = 0; 72 73 /** 74 * Query error information. 75 * 76 * Returns a string containing a description of the last occurred error or 77 * `null` if there is no error information. 78 * 79 * @function module:fs#error 80 * 81 * 82 * @returns {?string} 83 * 84 * @example 85 * // Trigger file system error 86 * unlink('/path/does/not/exist'); 87 * 88 * // Print error (should yield "No such file or directory") 89 * print(error(), "\n"); 90 */ 91 static uc_value_t * 92 uc_fs_error(uc_vm_t *vm, size_t nargs) 93 { 94 uc_value_t *errmsg; 95 96 if (last_error == 0) 97 return NULL; 98 99 errmsg = ucv_string_new(strerror(last_error)); 100 last_error = 0; 101 102 return errmsg; 103 } 104 105 static uc_value_t * 106 uc_fs_read_common(uc_vm_t *vm, size_t nargs, const char *type) 107 { 108 uc_value_t *limit = uc_fn_arg(0); 109 uc_value_t *rv = NULL; 110 char buf[128], *p = NULL, *tmp; 111 size_t rlen, len = 0; 112 const char *lstr; 113 int64_t lsize; 114 ssize_t llen; 115 116 FILE **fp = uc_fn_this(type); 117 118 if (!fp || !*fp) 119 err_return(EBADF); 120 121 if (ucv_type(limit) == UC_STRING) { 122 lstr = ucv_string_get(limit); 123 llen = ucv_string_length(limit); 124 125 if (llen == 4 && !strcmp(lstr, "line")) { 126 llen = getline(&p, &rlen, *fp); 127 128 if (llen == -1) 129 err_return(errno); 130 131 len = (size_t)llen; 132 } 133 else if (llen == 3 && !strcmp(lstr, "all")) { 134 while (true) { 135 rlen = fread(buf, 1, sizeof(buf), *fp); 136 137 tmp = realloc(p, len + rlen); 138 139 if (!tmp) { 140 free(p); 141 err_return(ENOMEM); 142 } 143 144 memcpy(tmp + len, buf, rlen); 145 146 p = tmp; 147 len += rlen; 148 149 if (rlen == 0) 150 break; 151 } 152 } 153 else if (llen == 1) { 154 llen = getdelim(&p, &rlen, *lstr, *fp); 155 156 if (llen == -1) 157 err_return(errno); 158 159 len = (size_t)llen; 160 } 161 else { 162 return NULL; 163 } 164 } 165 else if (ucv_type(limit) == UC_INTEGER) { 166 lsize = ucv_int64_get(limit); 167 168 if (lsize <= 0) 169 return NULL; 170 171 p = calloc(1, lsize); 172 173 if (!p) 174 err_return(ENOMEM); 175 176 len = fread(p, 1, lsize, *fp); 177 178 if (ferror(*fp)) { 179 free(p); 180 err_return(errno); 181 } 182 } 183 else { 184 err_return(EINVAL); 185 } 186 187 rv = ucv_string_new_length(p, len); 188 free(p); 189 190 return rv; 191 } 192 193 static uc_value_t * 194 uc_fs_write_common(uc_vm_t *vm, size_t nargs, const char *type) 195 { 196 uc_value_t *data = uc_fn_arg(0); 197 size_t len, wsize; 198 char *str; 199 200 FILE **fp = uc_fn_this(type); 201 202 if (!fp || !*fp) 203 err_return(EBADF); 204 205 if (ucv_type(data) == UC_STRING) { 206 len = ucv_string_length(data); 207 wsize = fwrite(ucv_string_get(data), 1, len, *fp); 208 } 209 else { 210 str = ucv_to_jsonstring(vm, data); 211 len = str ? strlen(str) : 0; 212 wsize = fwrite(str, 1, len, *fp); 213 free(str); 214 } 215 216 if (wsize < len && ferror(*fp)) 217 err_return(errno); 218 219 return ucv_int64_new(wsize); 220 } 221 222 static uc_value_t * 223 uc_fs_flush_common(uc_vm_t *vm, size_t nargs, const char *type) 224 { 225 FILE **fp = uc_fn_this(type); 226 227 if (!fp || !*fp) 228 err_return(EBADF); 229 230 if (fflush(*fp) != EOF) 231 err_return(errno); 232 233 return ucv_boolean_new(true); 234 } 235 236 static uc_value_t * 237 uc_fs_fileno_common(uc_vm_t *vm, size_t nargs, const char *type) 238 { 239 int fd; 240 241 FILE **fp = uc_fn_this(type); 242 243 if (!fp || !*fp) 244 err_return(EBADF); 245 246 fd = fileno(*fp); 247 248 if (fd == -1) 249 err_return(errno); 250 251 return ucv_int64_new(fd); 252 } 253 254 255 /** 256 * Represents a handle for interacting with a program launched by `popen()`. 257 * 258 * @class module:fs.proc 259 * @hideconstructor 260 * 261 * @borrows module:fs#error as module:fs.proc#error 262 * 263 * @see {@link module:fs#popen|popen()} 264 * 265 * @example 266 * 267 * const handle = popen(…); 268 * 269 * handle.read(…); 270 * handle.write(…); 271 * handle.flush(); 272 * 273 * handle.fileno(); 274 * 275 * handle.close(); 276 * 277 * handle.error(); 278 */ 279 280 /** 281 * Closes the program handle and awaits program termination. 282 * 283 * Upon calling `close()` on the handle, the program's input or output stream 284 * (depending on the open mode) is closed. Afterwards, the function awaits the 285 * termination of the underlying program and returns its exit code. 286 * 287 * - When the program was terminated by a signal, the return value will be the 288 * negative signal number, e.g. `-9` for SIGKILL. 289 * 290 * - When the program terminated normally, the return value will be the positive 291 * exit code of the program. 292 * 293 * Returns a negative signal number if the program was terminated by a signal. 294 * 295 * Returns a positive exit code if the program terminated normally. 296 * 297 * Returns `null` if an error occurred. 298 * 299 * @function module:fs.proc#close 300 * 301 * @returns {?number} 302 */ 303 static uc_value_t * 304 uc_fs_pclose(uc_vm_t *vm, size_t nargs) 305 { 306 FILE **fp = uc_fn_this("fs.proc"); 307 int rc; 308 309 if (!fp || !*fp) 310 err_return(EBADF); 311 312 rc = pclose(*fp); 313 *fp = NULL; 314 315 if (rc == -1) 316 err_return(errno); 317 318 if (WIFEXITED(rc)) 319 return ucv_int64_new(WEXITSTATUS(rc)); 320 321 if (WIFSIGNALED(rc)) 322 return ucv_int64_new(-WTERMSIG(rc)); 323 324 return ucv_int64_new(0); 325 } 326 327 /** 328 * Reads a chunk of data from the program handle. 329 * 330 * The length argument may be either a positive number of bytes to read, in 331 * which case the read call returns up to that many bytes, or a string to 332 * specify a dynamic read size. 333 * 334 * - If length is a number, the method will read the specified number of bytes 335 * from the handle. Reading stops after the given amount of bytes or after 336 * encountering EOF, whatever comes first. 337 * 338 * - If length is the string "line", the method will read an entire line, 339 * terminated by "\n" (a newline), from the handle. Reading stops at the next 340 * newline or when encountering EOF. The returned data will contain the 341 * terminating newline character if one was read. 342 * 343 * - If length is the string "all", the method will read from the handle until 344 * encountering EOF and return the complete contents. 345 * 346 * - If length is a single character string, the method will read from the 347 * handle until encountering the specified character or upon encountering 348 * EOF. The returned data will contain the terminating character if one was 349 * read. 350 * 351 * Returns a string containing the read data. 352 * 353 * Returns an empty string on EOF. 354 * 355 * Returns `null` if a read error occurred. 356 * 357 * @function module:fs.proc#read 358 * 359 * @param {number|string} length 360 * The length of data to read. Can be a number, the string "line", the string 361 * "all", or a single character string. 362 * 363 * @returns {?string} 364 * 365 * @example 366 * const fp = popen("command", "r"); 367 * 368 * // Example 1: Read 10 bytes from the handle 369 * const chunk = fp.read(10); 370 * 371 * // Example 2: Read the handle line by line 372 * for (let line = fp.read("line"); length(line); line = fp.read("line")) 373 * print(line); 374 * 375 * // Example 3: Read the complete contents from the handle 376 * const content = fp.read("all"); 377 * 378 * // Example 4: Read until encountering the character ':' 379 * const field = fp.read(":"); 380 */ 381 static uc_value_t * 382 uc_fs_pread(uc_vm_t *vm, size_t nargs) 383 { 384 return uc_fs_read_common(vm, nargs, "fs.proc"); 385 } 386 387 /** 388 * Writes a chunk of data to the program handle. 389 * 390 * In case the given data is not a string, it is converted to a string before 391 * being written to the program's stdin. String values are written as-is, 392 * integer and double values are written in decimal notation, boolean values are 393 * written as `true` or `false` while arrays and objects are converted to their 394 * JSON representation before being written. The `null` value is represented by 395 * an empty string so `proc.write(null)` would be a no-op. Resource values are 396 * written in the form `<type address>`, e.g. `<fs.file 0x7f60f0981760>`. 397 * 398 * If resource, array or object values contain a `tostring()` function in their 399 * prototypes, then this function is invoked to obtain an alternative string 400 * representation of the value. 401 * 402 * Returns the number of bytes written. 403 * 404 * Returns `null` if a write error occurred. 405 * 406 * @function module:fs.proc#write 407 * 408 * @param {*} data 409 * The data to be written. 410 * 411 * @returns {?number} 412 * 413 * @example 414 * const fp = popen("command", "w"); 415 * 416 * fp.write("Hello world!\n"); 417 */ 418 static uc_value_t * 419 uc_fs_pwrite(uc_vm_t *vm, size_t nargs) 420 { 421 return uc_fs_write_common(vm, nargs, "fs.proc"); 422 } 423 424 /** 425 * Forces a write of all buffered data to the underlying handle. 426 * 427 * Returns `true` if the data was successfully flushed. 428 * 429 * Returns `null` on error. 430 * 431 * @function module:fs.proc#flush 432 * 433 * @returns {?boolean} 434 * 435 */ 436 static uc_value_t * 437 uc_fs_pflush(uc_vm_t *vm, size_t nargs) 438 { 439 return uc_fs_flush_common(vm, nargs, "fs.proc"); 440 } 441 442 /** 443 * Obtains the number of the handle's underlying file descriptor. 444 * 445 * Returns the descriptor number. 446 * 447 * Returns `null` on error. 448 * 449 * @function module:fs.proc#fileno 450 * 451 * @returns {?number} 452 */ 453 static uc_value_t * 454 uc_fs_pfileno(uc_vm_t *vm, size_t nargs) 455 { 456 return uc_fs_fileno_common(vm, nargs, "fs.proc"); 457 } 458 459 /** 460 * Starts a process and returns a handle representing the executed process. 461 * 462 * The handle will be connected to the process stdin or stdout, depending on the 463 * value of the mode argument. 464 * 465 * The mode argument may be either "r" to open the process for reading (connect 466 * to its stdin) or "w" to open the process for writing (connect to its stdout). 467 * 468 * The mode character "r" or "w" may be optionally followed by "e" to apply the 469 * FD_CLOEXEC flag onto the open descriptor. 470 * 471 * Returns a process handle referring to the executed process. 472 * 473 * Returns `null` if an error occurred. 474 * 475 * @function module:fs#popen 476 * 477 * @param {string} command 478 * The command to be executed. 479 * 480 * @param {string} [mode="r"] 481 * The open mode of the process handle. 482 * 483 * @returns {?module:fs.proc} 484 * 485 * @example 486 * // Open a process 487 * const process = popen('command', 'r'); 488 */ 489 static uc_value_t * 490 uc_fs_popen(uc_vm_t *vm, size_t nargs) 491 { 492 uc_value_t *comm = uc_fn_arg(0); 493 uc_value_t *mode = uc_fn_arg(1); 494 FILE *fp; 495 496 if (ucv_type(comm) != UC_STRING) 497 err_return(EINVAL); 498 499 fp = popen(ucv_string_get(comm), 500 ucv_type(mode) == UC_STRING ? ucv_string_get(mode) : "r"); 501 502 if (!fp) 503 err_return(errno); 504 505 return uc_resource_new(proc_type, fp); 506 } 507 508 509 /** 510 * Represents a handle for interacting with a file opened by one of the file 511 * open functions. 512 * 513 * @class module:fs.file 514 * @hideconstructor 515 * 516 * @borrows module:fs#error as module:fs.file#error 517 * 518 * @see {@link module:fs#open|open()} 519 * @see {@link module:fs#fdopen|fdopen()} 520 * @see {@link module:fs#mkstemp|mkstemp()} 521 * @see {@link module:fs#pipe|pipe()} 522 * 523 * @example 524 * 525 * const handle = open(…); 526 * 527 * handle.read(…); 528 * handle.write(…); 529 * handle.flush(); 530 * 531 * handle.seek(…); 532 * handle.tell(); 533 * 534 * handle.isatty(); 535 * handle.fileno(); 536 * 537 * handle.close(); 538 * 539 * handle.error(); 540 */ 541 542 /** 543 * Closes the file handle. 544 * 545 * Upon calling `close()` on the handle, buffered data is flushed and the 546 * underlying file descriptor is closed. 547 * 548 * Returns `true` if the handle was properly closed. 549 * 550 * Returns `null` if an error occurred. 551 * 552 * @function module:fs.file#close 553 * 554 * @returns {?boolean} 555 */ 556 static uc_value_t * 557 uc_fs_close(uc_vm_t *vm, size_t nargs) 558 { 559 FILE **fp = uc_fn_this("fs.file"); 560 561 if (!fp || !*fp) 562 err_return(EBADF); 563 564 fclose(*fp); 565 *fp = NULL; 566 567 return ucv_boolean_new(true); 568 } 569 570 /** 571 * Reads a chunk of data from the file handle. 572 * 573 * The length argument may be either a positive number of bytes to read, in 574 * which case the read call returns up to that many bytes, or a string to 575 * specify a dynamic read size. 576 * 577 * - If length is a number, the method will read the specified number of bytes 578 * from the handle. Reading stops after the given amount of bytes or after 579 * encountering EOF, whatever comes first. 580 * 581 * - If length is the string "line", the method will read an entire line, 582 * terminated by "\n" (a newline), from the handle. Reading stops at the next 583 * newline or when encountering EOF. The returned data will contain the 584 * terminating newline character if one was read. 585 * 586 * - If length is the string "all", the method will read from the handle until 587 * encountering EOF and return the complete contents. 588 * 589 * - If length is a single character string, the method will read from the 590 * handle until encountering the specified character or upon encountering 591 * EOF. The returned data will contain the terminating character if one was 592 * read. 593 * 594 * Returns a string containing the read data. 595 * 596 * Returns an empty string on EOF. 597 * 598 * Returns `null` if a read error occurred. 599 * 600 * @function module:fs.file#read 601 * 602 * @param {number|string} length 603 * The length of data to read. Can be a number, the string "line", the string 604 * "all", or a single character string. 605 * 606 * @returns {?string} 607 * 608 * @example 609 * const fp = open("file.txt", "r"); 610 * 611 * // Example 1: Read 10 bytes from the handle 612 * const chunk = fp.read(10); 613 * 614 * // Example 2: Read the handle line by line 615 * for (let line = fp.read("line"); length(line); line = fp.read("line")) 616 * print(line); 617 * 618 * // Example 3: Read the complete contents from the handle 619 * const content = fp.read("all"); 620 * 621 * // Example 4: Read until encountering the character ':' 622 * const field = fp.read(":"); 623 */ 624 static uc_value_t * 625 uc_fs_read(uc_vm_t *vm, size_t nargs) 626 { 627 return uc_fs_read_common(vm, nargs, "fs.file"); 628 } 629 630 /** 631 * Writes a chunk of data to the file handle. 632 * 633 * In case the given data is not a string, it is converted to a string before 634 * being written into the file. String values are written as-is, integer and 635 * double values are written in decimal notation, boolean values are written as 636 * `true` or `false` while arrays and objects are converted to their JSON 637 * representation before being written. The `null` value is represented by an 638 * empty string so `file.write(null)` would be a no-op. Resource values are 639 * written in the form `<type address>`, e.g. `<fs.file 0x7f60f0981760>`. 640 * 641 * If resource, array or object values contain a `tostring()` function in their 642 * prototypes, then this function is invoked to obtain an alternative string 643 * representation of the value. 644 * 645 * Returns the number of bytes written. 646 * 647 * Returns `null` if a write error occurred. 648 * 649 * @function module:fs.file#write 650 * 651 * @param {*} data 652 * The data to be written. 653 * 654 * @returns {?number} 655 * 656 * @example 657 * const fp = open("file.txt", "w"); 658 * 659 * fp.write("Hello world!\n"); 660 */ 661 static uc_value_t * 662 uc_fs_write(uc_vm_t *vm, size_t nargs) 663 { 664 return uc_fs_write_common(vm, nargs, "fs.file"); 665 } 666 667 /** 668 * Set file read position. 669 * 670 * Set the read position of the open file handle to the given offset and 671 * position. 672 * 673 * Returns `true` if the read position was set. 674 * 675 * Returns `null` if an error occurred. 676 * 677 * @function module:fs.file#seek 678 * 679 * @param {number} [offset=0] 680 * The offset in bytes. 681 * 682 * @param {number} [position=0] 683 * The position of the offset. 684 * 685 * | Position | Description | 686 * |----------|----------------------------------------------------------------------------------------------| 687 * | `0` | The given offset is relative to the start of the file. This is the default value if omitted. | 688 * | `1` | The given offset is relative to the current read position. | 689 * | `2` | The given offset is relative to the end of the file. | 690 * 691 * @returns {?boolean} 692 * 693 * @example 694 * const fp = open("file.txt", "r"); 695 * 696 * print(fp.read(100), "\n"); // read 100 bytes... 697 * fp.seek(0, 0); // ... and reset position to start of file 698 * print(fp.read(100), "\n"); // ... read same 100 bytes again 699 * 700 * fp.seek(10, 1); // skip 10 bytes forward, relative to current offset ... 701 * fp.tell(); // ... position is at 110 now 702 * 703 * fp.seek(-10, 2); // set position to ten bytes before EOF ... 704 * print(fp.read(100), "\n"); // ... reads 10 bytes at most 705 */ 706 static uc_value_t * 707 uc_fs_seek(uc_vm_t *vm, size_t nargs) 708 { 709 uc_value_t *ofs = uc_fn_arg(0); 710 uc_value_t *how = uc_fn_arg(1); 711 int whence, res; 712 off_t offset; 713 714 FILE **fp = uc_fn_this("fs.file"); 715 716 if (!fp || !*fp) 717 err_return(EBADF); 718 719 if (!ofs) 720 offset = 0; 721 else if (ucv_type(ofs) != UC_INTEGER) 722 err_return(EINVAL); 723 else 724 offset = (off_t)ucv_int64_get(ofs); 725 726 if (!how) 727 whence = 0; 728 else if (ucv_type(how) != UC_INTEGER) 729 err_return(EINVAL); 730 else 731 whence = (int)ucv_int64_get(how); 732 733 res = fseeko(*fp, offset, whence); 734 735 if (res < 0) 736 err_return(errno); 737 738 return ucv_boolean_new(true); 739 } 740 741 /** 742 * Obtain current read position. 743 * 744 * Obtains the current, absolute read position of the open file. 745 * 746 * Returns an integer containing the current read offset in bytes. 747 * 748 * Returns `null` if an error occurred. 749 * 750 * @function module:fs.file#tell 751 * 752 * @returns {?number} 753 */ 754 static uc_value_t * 755 uc_fs_tell(uc_vm_t *vm, size_t nargs) 756 { 757 off_t offset; 758 759 FILE **fp = uc_fn_this("fs.file"); 760 761 if (!fp || !*fp) 762 err_return(EBADF); 763 764 offset = ftello(*fp); 765 766 if (offset < 0) 767 err_return(errno); 768 769 return ucv_int64_new(offset); 770 } 771 772 /** 773 * Check for TTY. 774 * 775 * Checks whether the open file handle refers to a TTY (terminal) device. 776 * 777 * Returns `true` if the handle refers to a terminal. 778 * 779 * Returns `false` if the handle refers to another kind of file. 780 * 781 * Returns `null` on error. 782 * 783 * @function module:fs.file#isatty 784 * 785 * @returns {?boolean} 786 * 787 */ 788 static uc_value_t * 789 uc_fs_isatty(uc_vm_t *vm, size_t nargs) 790 { 791 FILE **fp = uc_fn_this("fs.file"); 792 int fd; 793 794 if (!fp || !*fp) 795 err_return(EBADF); 796 797 fd = fileno(*fp); 798 799 if (fd == -1) 800 err_return(errno); 801 802 return ucv_boolean_new(isatty(fd) == 1); 803 } 804 805 /** 806 * Forces a write of all buffered data to the underlying handle. 807 * 808 * Returns `true` if the data was successfully flushed. 809 * 810 * Returns `null` on error. 811 * 812 * @function module:fs.file#flush 813 * 814 * @returns {?boolean} 815 * 816 */ 817 static uc_value_t * 818 uc_fs_flush(uc_vm_t *vm, size_t nargs) 819 { 820 return uc_fs_flush_common(vm, nargs, "fs.file"); 821 } 822 823 /** 824 * Obtains the number of the handle's underlying file descriptor. 825 * 826 * Returns the descriptor number. 827 * 828 * Returns `null` on error. 829 * 830 * @function module:fs.file#fileno 831 * 832 * @returns {?number} 833 */ 834 static uc_value_t * 835 uc_fs_fileno(uc_vm_t *vm, size_t nargs) 836 { 837 return uc_fs_fileno_common(vm, nargs, "fs.file"); 838 } 839 840 /** 841 * Opens a file. 842 * 843 * The mode argument specifies the way the file is opened, it may 844 * start with one of the following values: 845 * 846 * | Mode | Description | 847 * |---------|---------------------------------------------------------------------------------------------------------------| 848 * | "r" | Opens a file for reading. The file must exist. | 849 * | "w" | Opens a file for writing. If the file exists, it is truncated. If the file does not exist, it is created. | 850 * | "a" | Opens a file for appending. Data is written at the end of the file. If the file does not exist, it is created. | 851 * | "r+" | Opens a file for both reading and writing. The file must exist. | 852 * | "w+" | Opens a file for both reading and writing. If the file exists, it is truncated. If the file does not exist, it is created. | 853 * | "a+" | Opens a file for both reading and appending. Data can be read and written at the end of the file. If the file does not exist, it is created. | 854 * 855 * Additionally, the following flag characters may be appended to 856 * the mode value: 857 * 858 * | Flag | Description | 859 * |---------|---------------------------------------------------------------------------------------------------------------| 860 * | "x" | Opens a file for exclusive creation. If the file exists, the `open` call fails. | 861 * | "e" | Opens a file with the `O_CLOEXEC` flag set, ensuring that the file descriptor is closed on `exec` calls. | 862 * 863 * If the mode is one of `"w…"` or `"a…"`, the permission argument 864 * controls the filesystem permissions bits used when creating 865 * the file. 866 * 867 * Returns a file handle object associated with the opened file. 868 * 869 * @function module:fs#open 870 * 871 * @param {string} path 872 * The path to the file. 873 * 874 * @param {string} [mode="r"] 875 * The file opening mode. 876 * 877 * @param {number} [perm=0o666] 878 * The file creation permissions (for modes `w…` and `a…`) 879 * 880 * @returns {?module:fs.file} 881 * 882 * @example 883 * // Open a file in read-only mode 884 * const fileHandle = open('file.txt', 'r'); 885 */ 886 static uc_value_t * 887 uc_fs_open(uc_vm_t *vm, size_t nargs) 888 { 889 int open_mode, open_flags, fd, i; 890 uc_value_t *path = uc_fn_arg(0); 891 uc_value_t *mode = uc_fn_arg(1); 892 uc_value_t *perm = uc_fn_arg(2); 893 mode_t open_perm = 0666; 894 FILE *fp; 895 char *m; 896 897 if (ucv_type(path) != UC_STRING) 898 err_return(EINVAL); 899 900 m = (ucv_type(mode) == UC_STRING) ? ucv_string_get(mode) : "r"; 901 902 switch (*m) { 903 case 'r': 904 open_mode = O_RDONLY; 905 open_flags = 0; 906 break; 907 908 case 'w': 909 open_mode = O_WRONLY; 910 open_flags = O_CREAT | O_TRUNC; 911 break; 912 913 case 'a': 914 open_mode = O_WRONLY; 915 open_flags = O_CREAT | O_APPEND; 916 break; 917 918 default: 919 err_return(EINVAL); 920 } 921 922 for (i = 1; m[i]; i++) { 923 switch (m[i]) { 924 case '+': open_mode = O_RDWR; break; 925 case 'x': open_flags |= O_EXCL; break; 926 case 'e': open_flags |= O_CLOEXEC; break; 927 } 928 } 929 930 if (perm) { 931 if (ucv_type(perm) != UC_INTEGER) 932 err_return(EINVAL); 933 934 open_perm = ucv_int64_get(perm); 935 } 936 937 #ifdef O_LARGEFILE 938 open_flags |= open_mode | O_LARGEFILE; 939 #else 940 open_flags |= open_mode; 941 #endif 942 943 fd = open(ucv_string_get(path), open_flags, open_perm); 944 945 if (fd < 0) 946 return NULL; 947 948 fp = fdopen(fd, m); 949 950 if (!fp) { 951 i = errno; 952 close(fd); 953 err_return(i); 954 } 955 956 return uc_resource_new(file_type, fp); 957 } 958 959 /** 960 * Associates a file descriptor number with a file handle object. 961 * 962 * The mode argument controls how the file handle object is opened 963 * and must match the open mode of the underlying descriptor. 964 * 965 * It may be set to one of the following values: 966 * 967 * | Mode | Description | 968 * |---------|--------------------------------------------------------------------------------------------------------------| 969 * | "r" | Opens a file stream for reading. The file descriptor must be valid and opened in read mode. | 970 * | "w" | Opens a file stream for writing. The file descriptor must be valid and opened in write mode. | 971 * | "a" | Opens a file stream for appending. The file descriptor must be valid and opened in write mode. | 972 * | "r+" | Opens a file stream for both reading and writing. The file descriptor must be valid and opened in read/write mode. | 973 * | "w+" | Opens a file stream for both reading and writing. The file descriptor must be valid and opened in read/write mode. | 974 * | "a+" | Opens a file stream for both reading and appending. The file descriptor must be valid and opened in read/write mode. | 975 * 976 * Returns the file handle object associated with the file descriptor. 977 * 978 * @function module:fs#fdopen 979 * 980 * @param {number} fd 981 * The file descriptor. 982 * 983 * @param {string} [mode="r"] 984 * The open mode. 985 * 986 * @returns {Object} 987 * 988 * @example 989 * // Associate file descriptors of stdin and stdout with handles 990 * const stdinHandle = fdopen(0, 'r'); 991 * const stdoutHandle = fdopen(1, 'w'); 992 */ 993 static uc_value_t * 994 uc_fs_fdopen(uc_vm_t *vm, size_t nargs) 995 { 996 uc_value_t *fdno = uc_fn_arg(0); 997 uc_value_t *mode = uc_fn_arg(1); 998 int64_t n; 999 FILE *fp; 1000 1001 if (ucv_type(fdno) != UC_INTEGER) 1002 err_return(EINVAL); 1003 1004 n = ucv_int64_get(fdno); 1005 1006 if (n < 0 || n > INT_MAX) 1007 err_return(EBADF); 1008 1009 fp = fdopen((int)n, 1010 ucv_type(mode) == UC_STRING ? ucv_string_get(mode) : "r"); 1011 1012 if (!fp) 1013 err_return(errno); 1014 1015 return uc_resource_new(file_type, fp); 1016 } 1017 1018 1019 /** 1020 * Represents a handle for interacting with a directory opened by `opendir()`. 1021 * 1022 * @class module:fs.dir 1023 * @hideconstructor 1024 * 1025 * @borrows module:fs#error as module:fs.dir#error 1026 * 1027 * @see {@link module:fs#opendir|opendir()} 1028 * 1029 * @example 1030 * 1031 * const handle = opendir(…); 1032 * 1033 * handle.read(); 1034 * 1035 * handle.tell(); 1036 * handle.seek(…); 1037 * 1038 * handle.close(); 1039 * 1040 * handle.error(); 1041 */ 1042 1043 /** 1044 * Read the next entry from the open directory. 1045 * 1046 * Returns a string containing the entry name. 1047 * 1048 * Returns `null` if there are no more entries to read. 1049 * 1050 * Returns `null` if an error occurred. 1051 * 1052 * @function module:fs.dir#read 1053 * 1054 * @returns {?string} 1055 */ 1056 static uc_value_t * 1057 uc_fs_readdir(uc_vm_t *vm, size_t nargs) 1058 { 1059 DIR **dp = uc_fn_this("fs.dir"); 1060 struct dirent *e; 1061 1062 if (!dp || !*dp) 1063 err_return(EINVAL); 1064 1065 errno = 0; 1066 e = readdir(*dp); 1067 1068 if (!e) 1069 err_return(errno); 1070 1071 return ucv_string_new(e->d_name); 1072 } 1073 1074 /** 1075 * Obtain current read position. 1076 * 1077 * Returns the current read position in the open directory handle which can be 1078 * passed back to the `seek()` function to return to this position. This is 1079 * mainly useful to read an open directory handle (or specific items) multiple 1080 * times. 1081 * 1082 * Returns an integer referring to the current position. 1083 * 1084 * Returns `null` if an error occurred. 1085 * 1086 * @function module:fs.dir#tell 1087 * 1088 * @returns {?number} 1089 */ 1090 static uc_value_t * 1091 uc_fs_telldir(uc_vm_t *vm, size_t nargs) 1092 { 1093 DIR **dp = uc_fn_this("fs.dir"); 1094 long position; 1095 1096 if (!dp || !*dp) 1097 err_return(EBADF); 1098 1099 position = telldir(*dp); 1100 1101 if (position == -1) 1102 err_return(errno); 1103 1104 return ucv_int64_new((int64_t)position); 1105 } 1106 1107 /** 1108 * Set read position. 1109 * 1110 * Sets the read position within the open directory handle to the given offset 1111 * value. The offset value should be obtained by a previous call to `tell()` as 1112 * the specific integer values are implementation defined. 1113 * 1114 * Returns `true` if the read position was set. 1115 * 1116 * Returns `null` if an error occurred. 1117 * 1118 * @function module:fs.dir#seek 1119 * 1120 * @param {number} offset 1121 * Position value obtained by `tell()`. 1122 * 1123 * @returns {?boolean} 1124 * 1125 * @example 1126 * 1127 * const handle = opendir("/tmp"); 1128 * const begin = handle.tell(); 1129 * 1130 * print(handle.read(), "\n"); 1131 * 1132 * handle.seek(begin); 1133 * 1134 * print(handle.read(), "\n"); // prints the first entry again 1135 */ 1136 static uc_value_t * 1137 uc_fs_seekdir(uc_vm_t *vm, size_t nargs) 1138 { 1139 uc_value_t *ofs = uc_fn_arg(0); 1140 DIR **dp = uc_fn_this("fs.dir"); 1141 long position; 1142 1143 if (ucv_type(ofs) != UC_INTEGER) 1144 err_return(EINVAL); 1145 1146 if (!dp || !*dp) 1147 err_return(EBADF); 1148 1149 position = (long)ucv_int64_get(ofs); 1150 1151 seekdir(*dp, position); 1152 1153 return ucv_boolean_new(true); 1154 } 1155 1156 /** 1157 * Closes the directory handle. 1158 * 1159 * Closes the underlying file descriptor referring to the opened directory. 1160 * 1161 * Returns `true` if the handle was properly closed. 1162 * 1163 * Returns `null` if an error occurred. 1164 * 1165 * @function module:fs.dir#close 1166 * 1167 * @returns {?boolean} 1168 */ 1169 static uc_value_t * 1170 uc_fs_closedir(uc_vm_t *vm, size_t nargs) 1171 { 1172 DIR **dp = uc_fn_this("fs.dir"); 1173 1174 if (!dp || !*dp) 1175 err_return(EBADF); 1176 1177 closedir(*dp); 1178 *dp = NULL; 1179 1180 return ucv_boolean_new(true); 1181 } 1182 1183 /** 1184 * Opens a directory and returns a directory handle associated with the open 1185 * directory descriptor. 1186 * 1187 * Returns a director handle referring to the open directory. 1188 * 1189 * Returns `null` if an error occurred. 1190 * 1191 * @function module:fs#opendir 1192 * 1193 * @param {string} path 1194 * The path to the directory. 1195 * 1196 * @returns {?module:fs.dir} 1197 * 1198 * @example 1199 * // Open a directory 1200 * const directory = opendir('path/to/directory'); 1201 */ 1202 static uc_value_t * 1203 uc_fs_opendir(uc_vm_t *vm, size_t nargs) 1204 { 1205 uc_value_t *path = uc_fn_arg(0); 1206 DIR *dp; 1207 1208 if (ucv_type(path) != UC_STRING) 1209 err_return(EINVAL); 1210 1211 dp = opendir(ucv_string_get(path)); 1212 1213 if (!dp) 1214 err_return(errno); 1215 1216 return uc_resource_new(dir_type, dp); 1217 } 1218 1219 /** 1220 * Reads the target path of a symbolic link. 1221 * 1222 * Returns a string containing the target path. 1223 * 1224 * Returns `null` if an error occurred. 1225 * 1226 * @function module:fs#readlink 1227 * 1228 * @param {string} path 1229 * The path to the symbolic link. 1230 * 1231 * @returns {?string} 1232 * 1233 * @example 1234 * // Read the value of a symbolic link 1235 * const targetPath = readlink('symbolicLink'); 1236 */ 1237 static uc_value_t * 1238 uc_fs_readlink(uc_vm_t *vm, size_t nargs) 1239 { 1240 uc_value_t *path = uc_fn_arg(0); 1241 uc_value_t *res; 1242 ssize_t buflen = 0, rv; 1243 char *buf = NULL, *tmp; 1244 1245 if (ucv_type(path) != UC_STRING) 1246 err_return(EINVAL); 1247 1248 do { 1249 buflen += 128; 1250 tmp = realloc(buf, buflen); 1251 1252 if (!tmp) { 1253 free(buf); 1254 err_return(ENOMEM); 1255 } 1256 1257 buf = tmp; 1258 rv = readlink(ucv_string_get(path), buf, buflen); 1259 1260 if (rv == -1) { 1261 free(buf); 1262 err_return(errno); 1263 } 1264 1265 if (rv < buflen) 1266 break; 1267 } 1268 while (true); 1269 1270 res = ucv_string_new_length(buf, rv); 1271 1272 free(buf); 1273 1274 return res; 1275 } 1276 1277 /** 1278 * @typedef {Object} module:fs.FileStatResult 1279 * @property {Object} dev - The device information. 1280 * @property {number} dev.major - The major device number. 1281 * @property {number} dev.minor - The minor device number. 1282 * @property {Object} perm - The file permissions. 1283 * @property {boolean} perm.setuid - Whether the setuid bit is set. 1284 * @property {boolean} perm.setgid - Whether the setgid bit is set. 1285 * @property {boolean} perm.sticky - Whether the sticky bit is set. 1286 * @property {boolean} perm.user_read - Whether the file is readable by the owner. 1287 * @property {boolean} perm.user_write - Whether the file is writable by the owner. 1288 * @property {boolean} perm.user_exec - Whether the file is executable by the owner. 1289 * @property {boolean} perm.group_read - Whether the file is readable by the group. 1290 * @property {boolean} perm.group_write - Whether the file is writable by the group. 1291 * @property {boolean} perm.group_exec - Whether the file is executable by the group. 1292 * @property {boolean} perm.other_read - Whether the file is readable by others. 1293 * @property {boolean} perm.other_write - Whether the file is writable by others. 1294 * @property {boolean} perm.other_exec - Whether the file is executable by others. 1295 * @property {number} inode - The inode number. 1296 * @property {number} mode - The file mode. 1297 * @property {number} nlink - The number of hard links. 1298 * @property {number} uid - The user ID of the owner. 1299 * @property {number} gid - The group ID of the owner. 1300 * @property {number} size - The file size in bytes. 1301 * @property {number} blksize - The block size for file system I/O. 1302 * @property {number} blocks - The number of 512-byte blocks allocated for the file. 1303 * @property {number} atime - The timestamp when the file was last accessed. 1304 * @property {number} mtime - The timestamp when the file was last modified. 1305 * @property {number} ctime - The timestamp when the file status was last changed. 1306 * @property {string} type - The type of the file ("directory", "file", etc.). 1307 */ 1308 1309 static uc_value_t * 1310 uc_fs_stat_common(uc_vm_t *vm, size_t nargs, bool use_lstat) 1311 { 1312 uc_value_t *path = uc_fn_arg(0); 1313 uc_value_t *res, *o; 1314 struct stat st; 1315 int rv; 1316 1317 if (ucv_type(path) != UC_STRING) 1318 err_return(EINVAL); 1319 1320 rv = (use_lstat ? lstat : stat)(ucv_string_get(path), &st); 1321 1322 if (rv == -1) 1323 err_return(errno); 1324 1325 res = ucv_object_new(vm); 1326 1327 if (!res) 1328 err_return(ENOMEM); 1329 1330 o = ucv_object_new(vm); 1331 1332 if (o) { 1333 ucv_object_add(o, "major", ucv_int64_new(major(st.st_dev))); 1334 ucv_object_add(o, "minor", ucv_int64_new(minor(st.st_dev))); 1335 1336 ucv_object_add(res, "dev", o); 1337 } 1338 1339 o = ucv_object_new(vm); 1340 1341 if (o) { 1342 ucv_object_add(o, "setuid", ucv_boolean_new(st.st_mode & S_ISUID)); 1343 ucv_object_add(o, "setgid", ucv_boolean_new(st.st_mode & S_ISGID)); 1344 ucv_object_add(o, "sticky", ucv_boolean_new(st.st_mode & S_ISVTX)); 1345 1346 ucv_object_add(o, "user_read", ucv_boolean_new(st.st_mode & S_IRUSR)); 1347 ucv_object_add(o, "user_write", ucv_boolean_new(st.st_mode & S_IWUSR)); 1348 ucv_object_add(o, "user_exec", ucv_boolean_new(st.st_mode & S_IXUSR)); 1349 1350 ucv_object_add(o, "group_read", ucv_boolean_new(st.st_mode & S_IRGRP)); 1351 ucv_object_add(o, "group_write", ucv_boolean_new(st.st_mode & S_IWGRP)); 1352 ucv_object_add(o, "group_exec", ucv_boolean_new(st.st_mode & S_IXGRP)); 1353 1354 ucv_object_add(o, "other_read", ucv_boolean_new(st.st_mode & S_IROTH)); 1355 ucv_object_add(o, "other_write", ucv_boolean_new(st.st_mode & S_IWOTH)); 1356 ucv_object_add(o, "other_exec", ucv_boolean_new(st.st_mode & S_IXOTH)); 1357 1358 ucv_object_add(res, "perm", o); 1359 } 1360 1361 ucv_object_add(res, "inode", ucv_int64_new((int64_t)st.st_ino)); 1362 ucv_object_add(res, "mode", ucv_int64_new((int64_t)st.st_mode & ~S_IFMT)); 1363 ucv_object_add(res, "nlink", ucv_int64_new((int64_t)st.st_nlink)); 1364 ucv_object_add(res, "uid", ucv_int64_new((int64_t)st.st_uid)); 1365 ucv_object_add(res, "gid", ucv_int64_new((int64_t)st.st_gid)); 1366 ucv_object_add(res, "size", ucv_int64_new((int64_t)st.st_size)); 1367 ucv_object_add(res, "blksize", ucv_int64_new((int64_t)st.st_blksize)); 1368 ucv_object_add(res, "blocks", ucv_int64_new((int64_t)st.st_blocks)); 1369 ucv_object_add(res, "atime", ucv_int64_new((int64_t)st.st_atime)); 1370 ucv_object_add(res, "mtime", ucv_int64_new((int64_t)st.st_mtime)); 1371 ucv_object_add(res, "ctime", ucv_int64_new((int64_t)st.st_ctime)); 1372 1373 if (S_ISREG(st.st_mode)) 1374 ucv_object_add(res, "type", ucv_string_new("file")); 1375 else if (S_ISDIR(st.st_mode)) 1376 ucv_object_add(res, "type", ucv_string_new("directory")); 1377 else if (S_ISCHR(st.st_mode)) 1378 ucv_object_add(res, "type", ucv_string_new("char")); 1379 else if (S_ISBLK(st.st_mode)) 1380 ucv_object_add(res, "type", ucv_string_new("block")); 1381 else if (S_ISFIFO(st.st_mode)) 1382 ucv_object_add(res, "type", ucv_string_new("fifo")); 1383 else if (S_ISLNK(st.st_mode)) 1384 ucv_object_add(res, "type", ucv_string_new("link")); 1385 else if (S_ISSOCK(st.st_mode)) 1386 ucv_object_add(res, "type", ucv_string_new("socket")); 1387 else 1388 ucv_object_add(res, "type", ucv_string_new("unknown")); 1389 1390 return res; 1391 } 1392 1393 /** 1394 * Retrieves information about a file or directory. 1395 * 1396 * Returns an object containing information about the file or directory. 1397 * 1398 * Returns `null` if an error occurred, e.g. due to insufficient permissions. 1399 * 1400 * @function module:fs#stat 1401 * 1402 * @param {string} path 1403 * The path to the file or directory. 1404 * 1405 * @returns {?module:fs.FileStatResult} 1406 * 1407 * @example 1408 * // Get information about a file 1409 * const fileInfo = stat('path/to/file'); 1410 */ 1411 static uc_value_t * 1412 uc_fs_stat(uc_vm_t *vm, size_t nargs) 1413 { 1414 return uc_fs_stat_common(vm, nargs, false); 1415 } 1416 1417 /** 1418 * Retrieves information about a file or directory, without following symbolic 1419 * links. 1420 * 1421 * Returns an object containing information about the file or directory. 1422 * 1423 * Returns `null` if an error occurred, e.g. due to insufficient permissions. 1424 * 1425 * @function module:fs#lstat 1426 * 1427 * @param {string} path 1428 * The path to the file or directory. 1429 * 1430 * @returns {?module:fs.FileStatResult} 1431 * 1432 * @example 1433 * // Get information about a directory 1434 * const dirInfo = lstat('path/to/directory'); 1435 */ 1436 static uc_value_t * 1437 uc_fs_lstat(uc_vm_t *vm, size_t nargs) 1438 { 1439 return uc_fs_stat_common(vm, nargs, true); 1440 } 1441 1442 /** 1443 * Creates a new directory. 1444 * 1445 * Returns `true` if the directory was successfully created. 1446 * 1447 * Returns `null` if an error occurred, e.g. due to inexistent path. 1448 * 1449 * @function module:fs#mkdir 1450 * 1451 * @param {string} path 1452 * The path to the new directory. 1453 * 1454 * @returns {?boolean} 1455 * 1456 * @example 1457 * // Create a directory 1458 * mkdir('path/to/new-directory'); 1459 */ 1460 static uc_value_t * 1461 uc_fs_mkdir(uc_vm_t *vm, size_t nargs) 1462 { 1463 uc_value_t *path = uc_fn_arg(0); 1464 uc_value_t *mode = uc_fn_arg(1); 1465 1466 if (ucv_type(path) != UC_STRING || 1467 (mode && ucv_type(mode) != UC_INTEGER)) 1468 err_return(EINVAL); 1469 1470 if (mkdir(ucv_string_get(path), (mode_t)(mode ? ucv_int64_get(mode) : 0777)) == -1) 1471 err_return(errno); 1472 1473 return ucv_boolean_new(true); 1474 } 1475 1476 /** 1477 * Removes the specified directory. 1478 * 1479 * Returns `true` if the directory was successfully removed. 1480 * 1481 * Returns `null` if an error occurred, e.g. due to inexistent path. 1482 * 1483 * @function module:fs#rmdir 1484 * 1485 * @param {string} path 1486 * The path to the directory to be removed. 1487 * 1488 * @returns {?boolean} 1489 * 1490 * @example 1491 * // Remove a directory 1492 * rmdir('path/to/directory'); 1493 */ 1494 static uc_value_t * 1495 uc_fs_rmdir(uc_vm_t *vm, size_t nargs) 1496 { 1497 uc_value_t *path = uc_fn_arg(0); 1498 1499 if (ucv_type(path) != UC_STRING) 1500 err_return(EINVAL); 1501 1502 if (rmdir(ucv_string_get(path)) == -1) 1503 err_return(errno); 1504 1505 return ucv_boolean_new(true); 1506 } 1507 1508 /** 1509 * Creates a new symbolic link. 1510 * 1511 * Returns `true` if the symlink was successfully created. 1512 * 1513 * Returns `null` if an error occurred, e.g. due to inexistent path. 1514 * 1515 * @function module:fs#symlink 1516 * 1517 * @param {string} target 1518 * The target of the symbolic link. 1519 * 1520 * @param {string} path 1521 * The path of the symbolic link. 1522 * 1523 * @returns {?boolean} 1524 * 1525 * @example 1526 * // Create a symbolic link 1527 * symlink('target', 'path/to/symlink'); 1528 */ 1529 static uc_value_t * 1530 uc_fs_symlink(uc_vm_t *vm, size_t nargs) 1531 { 1532 uc_value_t *dest = uc_fn_arg(0); 1533 uc_value_t *path = uc_fn_arg(1); 1534 1535 if (ucv_type(dest) != UC_STRING || 1536 ucv_type(path) != UC_STRING) 1537 err_return(EINVAL); 1538 1539 if (symlink(ucv_string_get(dest), ucv_string_get(path)) == -1) 1540 err_return(errno); 1541 1542 return ucv_boolean_new(true); 1543 } 1544 1545 /** 1546 * Removes the specified file or symbolic link. 1547 * 1548 * Returns `true` if the unlink operation was successful. 1549 * 1550 * Returns `null` if an error occurred, e.g. due to inexistent path. 1551 * 1552 * @function module:fs#unlink 1553 * 1554 * @param {string} path 1555 * The path to the file or symbolic link. 1556 * 1557 * @returns {?boolean} 1558 * 1559 * @example 1560 * // Remove a file 1561 * unlink('path/to/file'); 1562 */ 1563 static uc_value_t * 1564 uc_fs_unlink(uc_vm_t *vm, size_t nargs) 1565 { 1566 uc_value_t *path = uc_fn_arg(0); 1567 1568 if (ucv_type(path) != UC_STRING) 1569 err_return(EINVAL); 1570 1571 if (unlink(ucv_string_get(path)) == -1) 1572 err_return(errno); 1573 1574 return ucv_boolean_new(true); 1575 } 1576 1577 /** 1578 * Retrieves the current working directory. 1579 * 1580 * Returns a string containing the current working directory path. 1581 * 1582 * Returns `null` if an error occurred. 1583 * 1584 * @function module:fs#getcwd 1585 * 1586 * @returns {?string} 1587 * 1588 * @example 1589 * // Get the current working directory 1590 * const cwd = getcwd(); 1591 */ 1592 static uc_value_t * 1593 uc_fs_getcwd(uc_vm_t *vm, size_t nargs) 1594 { 1595 uc_value_t *res; 1596 char *buf = NULL, *tmp; 1597 size_t buflen = 0; 1598 1599 do { 1600 buflen += 128; 1601 tmp = realloc(buf, buflen); 1602 1603 if (!tmp) { 1604 free(buf); 1605 err_return(ENOMEM); 1606 } 1607 1608 buf = tmp; 1609 1610 if (getcwd(buf, buflen) != NULL) 1611 break; 1612 1613 if (errno == ERANGE) 1614 continue; 1615 1616 free(buf); 1617 err_return(errno); 1618 } 1619 while (true); 1620 1621 res = ucv_string_new(buf); 1622 1623 free(buf); 1624 1625 return res; 1626 } 1627 1628 /** 1629 * Changes the current working directory to the specified path. 1630 * 1631 * Returns `true` if the permission change was successful. 1632 * 1633 * Returns `null` if an error occurred, e.g. due to insufficient permissions or 1634 * invalid arguments. 1635 * 1636 * @function module:fs#chdir 1637 * 1638 * @param {string} path 1639 * The path to the new working directory. 1640 * 1641 * @returns {?boolean} 1642 * 1643 * @example 1644 * // Change the current working directory 1645 * chdir('new-directory'); 1646 */ 1647 static uc_value_t * 1648 uc_fs_chdir(uc_vm_t *vm, size_t nargs) 1649 { 1650 uc_value_t *path = uc_fn_arg(0); 1651 1652 if (ucv_type(path) != UC_STRING) 1653 err_return(EINVAL); 1654 1655 if (chdir(ucv_string_get(path)) == -1) 1656 err_return(errno); 1657 1658 return ucv_boolean_new(true); 1659 } 1660 1661 /** 1662 * Changes the permission mode bits of a file or directory. 1663 * 1664 * Returns `true` if the permission change was successful. 1665 * 1666 * Returns `null` if an error occurred, e.g. due to insufficient permissions or 1667 * invalid arguments. 1668 * 1669 * @function module:fs#chmod 1670 * 1671 * @param {string} path 1672 * The path to the file or directory. 1673 * 1674 * @param {number} mode 1675 * The new mode (permissions). 1676 * 1677 * @returns {?boolean} 1678 * 1679 * @example 1680 * // Change the mode of a file 1681 * chmod('path/to/file', 0o644); 1682 */ 1683 static uc_value_t * 1684 uc_fs_chmod(uc_vm_t *vm, size_t nargs) 1685 { 1686 uc_value_t *path = uc_fn_arg(0); 1687 uc_value_t *mode = uc_fn_arg(1); 1688 1689 if (ucv_type(path) != UC_STRING || 1690 ucv_type(mode) != UC_INTEGER) 1691 err_return(EINVAL); 1692 1693 if (chmod(ucv_string_get(path), (mode_t)ucv_int64_get(mode)) == -1) 1694 err_return(errno); 1695 1696 return ucv_boolean_new(true); 1697 } 1698 1699 static bool 1700 uc_fs_resolve_user(uc_value_t *v, uid_t *uid) 1701 { 1702 struct passwd *pw = NULL; 1703 int64_t n; 1704 char *s; 1705 1706 *uid = (uid_t)-1; 1707 1708 switch (ucv_type(v)) { 1709 case UC_INTEGER: 1710 n = ucv_int64_get(v); 1711 1712 if (n < -1) { 1713 errno = ERANGE; 1714 1715 return false; 1716 } 1717 1718 *uid = (uid_t)n; 1719 1720 return true; 1721 1722 case UC_STRING: 1723 s = ucv_string_get(v); 1724 pw = getpwnam(s); 1725 1726 if (!pw) { 1727 errno = ENOENT; 1728 1729 return false; 1730 } 1731 1732 *uid = pw->pw_uid; 1733 1734 return true; 1735 1736 case UC_NULL: 1737 return true; 1738 1739 default: 1740 errno = EINVAL; 1741 1742 return false; 1743 } 1744 } 1745 1746 static bool 1747 uc_fs_resolve_group(uc_value_t *v, gid_t *gid) 1748 { 1749 struct group *gr = NULL; 1750 int64_t n; 1751 char *s; 1752 1753 *gid = (gid_t)-1; 1754 1755 switch (ucv_type(v)) { 1756 case UC_INTEGER: 1757 n = ucv_int64_get(v); 1758 1759 if (n < -1) { 1760 errno = ERANGE; 1761 1762 return false; 1763 } 1764 1765 *gid = (gid_t)n; 1766 1767 return true; 1768 1769 case UC_STRING: 1770 s = ucv_string_get(v); 1771 gr = getgrnam(s); 1772 1773 if (!gr) { 1774 errno = ENOENT; 1775 1776 return false; 1777 } 1778 1779 *gid = gr->gr_gid; 1780 1781 return true; 1782 1783 case UC_NULL: 1784 return true; 1785 1786 default: 1787 errno = EINVAL; 1788 1789 return false; 1790 } 1791 } 1792 1793 /** 1794 * Changes the owner and group of a file or directory. 1795 * 1796 * The user and group may be specified either as uid or gid number respectively, 1797 * or as a string containing the user or group name, in which case it is 1798 * resolved to the proper uid/gid first. 1799 * 1800 * If either the user or group parameter is omitted or given as `-1`, 1801 * it is not changed. 1802 * 1803 * Returns `true` if the ownership change was successful. 1804 * 1805 * Returns `null` if an error occurred or if a user/group name cannot be 1806 * resolved to a uid/gid value. 1807 * 1808 * @function module:fs#chown 1809 * 1810 * @param {string} path 1811 * The path to the file or directory. 1812 * 1813 * @param {number|string} [uid=-1] 1814 * The new owner's user ID. When given as number, it is used as-is, when given 1815 * as string, the user name is resolved to the corresponding uid first. 1816 * 1817 * @param {number|string} [gid=-1] 1818 * The new group's ID. When given as number, it is used as-is, when given as 1819 * string, the group name is resolved to the corresponding gid first. 1820 * 1821 * @returns {?boolean} 1822 * 1823 * @example 1824 * // Change the owner of a file 1825 * chown('path/to/file', 1000); 1826 * 1827 * // Change the group of a directory 1828 * chown('/htdocs/', null, 'www-data'); 1829 */ 1830 static uc_value_t * 1831 uc_fs_chown(uc_vm_t *vm, size_t nargs) 1832 { 1833 uc_value_t *path = uc_fn_arg(0); 1834 uc_value_t *user = uc_fn_arg(1); 1835 uc_value_t *group = uc_fn_arg(2); 1836 uid_t uid; 1837 gid_t gid; 1838 1839 if (ucv_type(path) != UC_STRING) 1840 err_return(EINVAL); 1841 1842 if (!uc_fs_resolve_user(user, &uid) || 1843 !uc_fs_resolve_group(group, &gid)) 1844 err_return(errno); 1845 1846 if (chown(ucv_string_get(path), uid, gid) == -1) 1847 err_return(errno); 1848 1849 return ucv_boolean_new(true); 1850 } 1851 1852 /** 1853 * Renames or moves a file or directory. 1854 * 1855 * Returns `true` if the rename operation was successful. 1856 * 1857 * Returns `null` if an error occurred. 1858 * 1859 * @function module:fs#rename 1860 * 1861 * @param {string} oldPath 1862 * The current path of the file or directory. 1863 * 1864 * @param {string} newPath 1865 * The new path of the file or directory. 1866 * 1867 * @returns {?boolean} 1868 * 1869 * @example 1870 * // Rename a file 1871 * rename('old-name.txt', 'new-name.txt'); 1872 */ 1873 static uc_value_t * 1874 uc_fs_rename(uc_vm_t *vm, size_t nargs) 1875 { 1876 uc_value_t *oldpath = uc_fn_arg(0); 1877 uc_value_t *newpath = uc_fn_arg(1); 1878 1879 if (ucv_type(oldpath) != UC_STRING || 1880 ucv_type(newpath) != UC_STRING) 1881 err_return(EINVAL); 1882 1883 if (rename(ucv_string_get(oldpath), ucv_string_get(newpath))) 1884 err_return(errno); 1885 1886 return ucv_boolean_new(true); 1887 } 1888 1889 static uc_value_t * 1890 uc_fs_glob(uc_vm_t *vm, size_t nargs) 1891 { 1892 uc_value_t *pat, *arr; 1893 glob_t gl = { 0 }; 1894 size_t i; 1895 1896 for (i = 0; i < nargs; i++) { 1897 pat = uc_fn_arg(i); 1898 1899 if (ucv_type(pat) != UC_STRING) { 1900 globfree(&gl); 1901 err_return(EINVAL); 1902 } 1903 1904 glob(ucv_string_get(pat), i ? GLOB_APPEND : 0, NULL, &gl); 1905 } 1906 1907 arr = ucv_array_new(vm); 1908 1909 for (i = 0; i < gl.gl_pathc; i++) 1910 ucv_array_push(arr, ucv_string_new(gl.gl_pathv[i])); 1911 1912 globfree(&gl); 1913 1914 return arr; 1915 } 1916 1917 /** 1918 * Retrieves the directory name of a path. 1919 * 1920 * Returns the directory name component of the specified path. 1921 * 1922 * Returns `null` if the path argument is not a string. 1923 * 1924 * @function module:fs#dirname 1925 * 1926 * @param {string} path 1927 * The path to extract the directory name from. 1928 * 1929 * @returns {?string} 1930 * 1931 * @example 1932 * // Get the directory name of a path 1933 * const directoryName = dirname('/path/to/file.txt'); 1934 */ 1935 static uc_value_t * 1936 uc_fs_dirname(uc_vm_t *vm, size_t nargs) 1937 { 1938 uc_value_t *path = uc_fn_arg(0); 1939 size_t i; 1940 char *s; 1941 1942 if (ucv_type(path) != UC_STRING) 1943 err_return(EINVAL); 1944 1945 i = ucv_string_length(path); 1946 s = ucv_string_get(path); 1947 1948 if (i == 0) 1949 return ucv_string_new("."); 1950 1951 for (i--; s[i] == '/'; i--) 1952 if (i == 0) 1953 return ucv_string_new("/"); 1954 1955 for (; s[i] != '/'; i--) 1956 if (i == 0) 1957 return ucv_string_new("."); 1958 1959 for (; s[i] == '/'; i--) 1960 if (i == 0) 1961 return ucv_string_new("/"); 1962 1963 return ucv_string_new_length(s, i + 1); 1964 } 1965 1966 /** 1967 * Retrieves the base name of a path. 1968 * 1969 * Returns the base name component of the specified path. 1970 * 1971 * Returns `null` if the path argument is not a string. 1972 * 1973 * @function module:fs#basename 1974 * 1975 * @param {string} path 1976 * The path to extract the base name from. 1977 * 1978 * @returns {?string} 1979 * 1980 * @example 1981 * // Get the base name of a path 1982 * const baseName = basename('/path/to/file.txt'); 1983 */ 1984 static uc_value_t * 1985 uc_fs_basename(uc_vm_t *vm, size_t nargs) 1986 { 1987 uc_value_t *path = uc_fn_arg(0); 1988 size_t i, len, skip; 1989 char *s; 1990 1991 if (ucv_type(path) != UC_STRING) 1992 err_return(EINVAL); 1993 1994 len = ucv_string_length(path); 1995 s = ucv_string_get(path); 1996 1997 if (len == 0) 1998 return ucv_string_new("."); 1999 2000 for (i = len - 1, skip = 0; i > 0 && s[i] == '/'; i--, skip++) 2001 ; 2002 2003 for (; i > 0 && s[i - 1] != '/'; i--) 2004 ; 2005 2006 return ucv_string_new_length(s + i, len - i - skip); 2007 } 2008 2009 static int 2010 uc_fs_lsdir_sort_fn(const void *k1, const void *k2) 2011 { 2012 uc_value_t * const *v1 = k1; 2013 uc_value_t * const *v2 = k2; 2014 2015 return strcmp(ucv_string_get(*v1), ucv_string_get(*v2)); 2016 } 2017 2018 /** 2019 * Lists the content of a directory. 2020 * 2021 * Returns a sorted array of the names of files and directories in the specified 2022 * directory. 2023 * 2024 * Returns `null` if an error occurred, e.g. if the specified directory cannot 2025 * be opened. 2026 * 2027 * @function module:fs#lsdir 2028 * 2029 * @param {string} path 2030 * The path to the directory. 2031 * 2032 * @returns {?string[]} 2033 * 2034 * @example 2035 * // List the content of a directory 2036 * const fileList = lsdir('/path/to/directory'); 2037 */ 2038 static uc_value_t * 2039 uc_fs_lsdir(uc_vm_t *vm, size_t nargs) 2040 { 2041 uc_value_t *path = uc_fn_arg(0); 2042 uc_value_t *pat = uc_fn_arg(1); 2043 uc_value_t *res = NULL; 2044 uc_regexp_t *reg; 2045 struct dirent *e; 2046 DIR *d; 2047 2048 if (ucv_type(path) != UC_STRING) 2049 err_return(EINVAL); 2050 2051 switch (ucv_type(pat)) { 2052 case UC_NULL: 2053 case UC_STRING: 2054 case UC_REGEXP: 2055 break; 2056 2057 default: 2058 err_return(EINVAL); 2059 } 2060 2061 d = opendir(ucv_string_get(path)); 2062 2063 if (!d) 2064 err_return(errno); 2065 2066 res = ucv_array_new(vm); 2067 2068 while ((e = readdir(d)) != NULL) { 2069 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) 2070 continue; 2071 2072 if (ucv_type(pat) == UC_REGEXP) { 2073 reg = (uc_regexp_t *)pat; 2074 2075 if (regexec(®->regexp, e->d_name, 0, NULL, 0) == REG_NOMATCH) 2076 continue; 2077 } 2078 else if (ucv_type(pat) == UC_STRING) { 2079 if (fnmatch(ucv_string_get(pat), e->d_name, 0) == FNM_NOMATCH) 2080 continue; 2081 } 2082 2083 ucv_array_push(res, ucv_string_new(e->d_name)); 2084 } 2085 2086 closedir(d); 2087 2088 ucv_array_sort(res, uc_fs_lsdir_sort_fn); 2089 2090 return res; 2091 } 2092 2093 /** 2094 * Creates a unique, ephemeral temporary file. 2095 * 2096 * Creates a new temporary file, opens it in read and write mode, unlinks it and 2097 * returns a file handle object referring to the yet open but deleted file. 2098 * 2099 * Upon closing the handle, the associated file will automatically vanish from 2100 * the system. 2101 * 2102 * The optional path template argument may be used to override the path and name 2103 * chosen for the temporary file. If the path template contains no path element, 2104 * `/tmp/` is prepended, if it does not end with `XXXXXX`, then * `.XXXXXX` is 2105 * appended to it. The `XXXXXX` sequence is replaced with a random value 2106 * ensuring uniqueness of the temporary file name. 2107 * 2108 * Returns a file handle object referring to the ephemeral file on success. 2109 * 2110 * Returns `null` if an error occurred, e.g. on insufficient permissions or 2111 * inaccessible directory. 2112 * 2113 * @function module:fs#mkstemp 2114 * 2115 * @param {string} [template="/tmp/XXXXXX"] 2116 * The path template to use when forming the temporary file name. 2117 * 2118 * @returns {?module:fs.file} 2119 * 2120 * @example 2121 * // Create a unique temporary file in the current working directory 2122 * const tempFile = mkstemp('./data-XXXXXX'); 2123 */ 2124 static uc_value_t * 2125 uc_fs_mkstemp(uc_vm_t *vm, size_t nargs) 2126 { 2127 uc_value_t *template = uc_fn_arg(0); 2128 bool ends_with_template = false; 2129 char *path, *t; 2130 FILE *fp; 2131 size_t l; 2132 int fd; 2133 2134 if (template && ucv_type(template) != UC_STRING) 2135 err_return(EINVAL); 2136 2137 t = ucv_string_get(template); 2138 l = ucv_string_length(template); 2139 2140 ends_with_template = (l >= 6 && strcmp(&t[l - 6], "XXXXXX") == 0); 2141 2142 if (t && strchr(t, '/')) { 2143 if (ends_with_template) 2144 xasprintf(&path, "%s", t); 2145 else 2146 xasprintf(&path, "%s.XXXXXX", t); 2147 } 2148 else if (t) { 2149 if (ends_with_template) 2150 xasprintf(&path, "/tmp/%s", t); 2151 else 2152 xasprintf(&path, "/tmp/%s.XXXXXX", t); 2153 } 2154 else { 2155 xasprintf(&path, "/tmp/XXXXXX"); 2156 } 2157 2158 do { 2159 fd = mkstemp(path); 2160 } 2161 while (fd == -1 && errno == EINTR); 2162 2163 if (fd == -1) { 2164 free(path); 2165 err_return(errno); 2166 } 2167 2168 unlink(path); 2169 free(path); 2170 2171 fp = fdopen(fd, "r+"); 2172 2173 if (!fp) { 2174 close(fd); 2175 err_return(errno); 2176 } 2177 2178 return uc_resource_new(file_type, fp); 2179 } 2180 2181 /** 2182 * Checks the accessibility of a file or directory. 2183 * 2184 * The optional modes argument specifies the access modes which should be 2185 * checked. A file is only considered accessible if all access modes specified 2186 * in the modes argument are possible. 2187 * 2188 * The following modes are recognized: 2189 * 2190 * | Mode | Description | 2191 * |------|---------------------------------------| 2192 * | "r" | Tests whether the file is readable. | 2193 * | "w" | Tests whether the file is writable. | 2194 * | "x" | Tests whether the file is executable. | 2195 * | "f" | Tests whether the file exists. | 2196 * 2197 * Returns `true` if the given path is accessible or `false` when it is not. 2198 * 2199 * Returns `null` if an error occurred, e.g. due to inaccessible intermediate 2200 * path components, invalid path arguments etc. 2201 * 2202 * @function module:fs#access 2203 * 2204 * @param {string} path 2205 * The path to the file or directory. 2206 * 2207 * @param {number} [mode="f"] 2208 * Optional access mode. 2209 * 2210 * @returns {?boolean} 2211 * 2212 * @example 2213 * // Check file read and write accessibility 2214 * const isAccessible = access('path/to/file', 'rw'); 2215 * 2216 * // Check execute permissions 2217 * const mayExecute = access('/usr/bin/example', 'x'); 2218 */ 2219 static uc_value_t * 2220 uc_fs_access(uc_vm_t *vm, size_t nargs) 2221 { 2222 uc_value_t *path = uc_fn_arg(0); 2223 uc_value_t *test = uc_fn_arg(1); 2224 int mode = F_OK; 2225 char *p; 2226 2227 if (ucv_type(path) != UC_STRING) 2228 err_return(EINVAL); 2229 2230 if (test && ucv_type(test) != UC_STRING) 2231 err_return(EINVAL); 2232 2233 for (p = ucv_string_get(test); p && *p; p++) { 2234 switch (*p) { 2235 case 'r': 2236 mode |= R_OK; 2237 break; 2238 2239 case 'w': 2240 mode |= W_OK; 2241 break; 2242 2243 case 'x': 2244 mode |= X_OK; 2245 break; 2246 2247 case 'f': 2248 mode |= F_OK; 2249 break; 2250 2251 default: 2252 err_return(EINVAL); 2253 } 2254 } 2255 2256 if (access(ucv_string_get(path), mode) == -1) 2257 err_return(errno); 2258 2259 return ucv_boolean_new(true); 2260 } 2261 2262 /** 2263 * Reads the content of a file, optionally limited to the given amount of bytes. 2264 * 2265 * Returns a string containing the file contents. 2266 * 2267 * Returns `null` if an error occurred, e.g. due to insufficient permissions. 2268 * 2269 * @function module:fs#readfile 2270 * 2271 * @param {string} path 2272 * The path to the file. 2273 * 2274 * @param {number} [limit] 2275 * Number of bytes to limit the result to. When omitted, the entire content is 2276 * returned. 2277 * 2278 * @returns {?string} 2279 * 2280 * @example 2281 * // Read first 100 bytes of content 2282 * const content = readfile('path/to/file', 100); 2283 * 2284 * // Read entire file content 2285 * const content = readfile('path/to/file'); 2286 */ 2287 static uc_value_t * 2288 uc_fs_readfile(uc_vm_t *vm, size_t nargs) 2289 { 2290 uc_value_t *path = uc_fn_arg(0); 2291 uc_value_t *size = uc_fn_arg(1); 2292 uc_value_t *res = NULL; 2293 uc_stringbuf_t *buf; 2294 ssize_t limit = -1; 2295 size_t rlen, blen; 2296 FILE *fp; 2297 2298 if (ucv_type(path) != UC_STRING) 2299 err_return(EINVAL); 2300 2301 if (size) { 2302 if (ucv_type(size) != UC_INTEGER) 2303 err_return(EINVAL); 2304 2305 limit = ucv_int64_get(size); 2306 } 2307 2308 fp = fopen(ucv_string_get(path), "r"); 2309 2310 if (!fp) 2311 err_return(errno); 2312 2313 buf = ucv_stringbuf_new(); 2314 2315 if (limit > -1 && limit < BUFSIZ) 2316 setvbuf(fp, NULL, _IONBF, 0); 2317 2318 while (limit != 0) { 2319 blen = 1024; 2320 2321 if (limit > 0 && blen > (size_t)limit) 2322 blen = (size_t)limit; 2323 2324 printbuf_memset(buf, printbuf_length(buf) + blen - 1, 0, 1); 2325 2326 buf->bpos -= blen; 2327 rlen = fread(buf->buf + buf->bpos, 1, blen, fp); 2328 buf->bpos += rlen; 2329 2330 if (rlen < blen) 2331 break; 2332 2333 if (limit > 0) 2334 limit -= rlen; 2335 } 2336 2337 if (ferror(fp)) { 2338 fclose(fp); 2339 printbuf_free(buf); 2340 err_return(errno); 2341 } 2342 2343 fclose(fp); 2344 2345 /* add sentinel null byte but don't count it towards the string length */ 2346 printbuf_memappend_fast(buf, "\0", 1); 2347 res = ucv_stringbuf_finish(buf); 2348 ((uc_string_t *)res)->length--; 2349 2350 return res; 2351 } 2352 2353 /** 2354 * Writes the given data to a file, optionally truncated to the given amount 2355 * of bytes. 2356 * 2357 * In case the given data is not a string, it is converted to a string before 2358 * being written into the file. String values are written as-is, integer and 2359 * double values are written in decimal notation, boolean values are written as 2360 * `true` or `false` while arrays and objects are converted to their JSON 2361 * representation before being written into the file. The `null` value is 2362 * represented by an empty string so `writefile(…, null)` would write an empty 2363 * file. Resource values are written in the form `<type address>`, e.g. 2364 * `<fs.file 0x7f60f0981760>`. 2365 * 2366 * If resource, array or object values contain a `tostring()` function in their 2367 * prototypes, then this function is invoked to obtain an alternative string 2368 * representation of the value. 2369 * 2370 * If a file already exists at the given path, it is truncated. If no file 2371 * exists, it is created with default permissions 0o666 masked by the currently 2372 * effective umask. 2373 * 2374 * Returns the number of bytes written. 2375 * 2376 * Returns `null` if an error occurred, e.g. due to insufficient permissions. 2377 * 2378 * @function module:fs#writefile 2379 * 2380 * @param {string} path 2381 * The path to the file. 2382 * 2383 * @param {*} data 2384 * The data to be written. 2385 * 2386 * @param {number} [limit] 2387 * Truncates the amount of data to be written to the specified amount of bytes. 2388 * When omitted, the entire content is written. 2389 * 2390 * @returns {?number} 2391 * 2392 * @example 2393 * // Write string to a file 2394 * const bytesWritten = writefile('path/to/file', 'Hello, World!'); 2395 * 2396 * // Write object as JSON to a file and limit to 1024 bytes at most 2397 * const obj = { foo: "Hello world", bar: true, baz: 123 }; 2398 * const bytesWritten = writefile('debug.txt', obj, 1024); 2399 */ 2400 static uc_value_t * 2401 uc_fs_writefile(uc_vm_t *vm, size_t nargs) 2402 { 2403 uc_value_t *path = uc_fn_arg(0); 2404 uc_value_t *data = uc_fn_arg(1); 2405 uc_value_t *size = uc_fn_arg(2); 2406 uc_stringbuf_t *buf = NULL; 2407 ssize_t limit = -1; 2408 size_t wlen = 0; 2409 int err = 0; 2410 FILE *fp; 2411 2412 if (ucv_type(path) != UC_STRING) 2413 err_return(EINVAL); 2414 2415 if (size) { 2416 if (ucv_type(size) != UC_INTEGER) 2417 err_return(EINVAL); 2418 2419 limit = ucv_int64_get(size); 2420 } 2421 2422 fp = fopen(ucv_string_get(path), "w"); 2423 2424 if (!fp) 2425 err_return(errno); 2426 2427 if (data && ucv_type(data) != UC_STRING) { 2428 buf = xprintbuf_new(); 2429 ucv_to_stringbuf_formatted(vm, buf, data, 0, '\0', 0); 2430 2431 if (limit < 0 || limit > printbuf_length(buf)) 2432 limit = printbuf_length(buf); 2433 2434 wlen = fwrite(buf->buf, 1, limit, fp); 2435 2436 if (wlen < (size_t)limit) 2437 err = errno; 2438 2439 printbuf_free(buf); 2440 } 2441 else if (data) { 2442 if (limit < 0 || (size_t)limit > ucv_string_length(data)) 2443 limit = ucv_string_length(data); 2444 2445 wlen = fwrite(ucv_string_get(data), 1, limit, fp); 2446 2447 if (wlen < (size_t)limit) 2448 err = errno; 2449 } 2450 2451 fclose(fp); 2452 2453 if (err) 2454 err_return(err); 2455 2456 return ucv_uint64_new(wlen); 2457 } 2458 2459 /** 2460 * Resolves the absolute path of a file or directory. 2461 * 2462 * Returns a string containing the resolved path. 2463 * 2464 * Returns `null` if an error occurred, e.g. due to insufficient permissions. 2465 * 2466 * @function module:fs#realpath 2467 * 2468 * @param {string} path 2469 * The path to the file or directory. 2470 * 2471 * @returns {?string} 2472 * 2473 * @example 2474 * // Resolve the absolute path of a file 2475 * const absolutePath = realpath('path/to/file', 'utf8'); 2476 */ 2477 static uc_value_t * 2478 uc_fs_realpath(uc_vm_t *vm, size_t nargs) 2479 { 2480 uc_value_t *path = uc_fn_arg(0), *rv; 2481 char *resolved; 2482 2483 if (ucv_type(path) != UC_STRING) 2484 err_return(EINVAL); 2485 2486 resolved = realpath(ucv_string_get(path), NULL); 2487 2488 if (!resolved) 2489 err_return(errno); 2490 2491 rv = ucv_string_new(resolved); 2492 2493 free(resolved); 2494 2495 return rv; 2496 } 2497 2498 /** 2499 * Creates a pipe and returns file handle objects associated with the read- and 2500 * write end of the pipe respectively. 2501 * 2502 * Returns a two element array containing both a file handle object open in read 2503 * mode referring to the read end of the pipe and a file handle object open in 2504 * write mode referring to the write end of the pipe. 2505 * 2506 * Returns `null` if an error occurred. 2507 * 2508 * @function module:fs#pipe 2509 * 2510 * @returns {?module:fs.file[]} 2511 * 2512 * @example 2513 * // Create a pipe 2514 * const pipeHandles = pipe(); 2515 * pipeHandles[1].write("Hello world\n"); 2516 * print(pipeHandles[0].read("line")); 2517 */ 2518 static uc_value_t * 2519 uc_fs_pipe(uc_vm_t *vm, size_t nargs) 2520 { 2521 int pfds[2], err; 2522 FILE *rfp, *wfp; 2523 uc_value_t *rv; 2524 2525 if (pipe(pfds) == -1) 2526 err_return(errno); 2527 2528 rfp = fdopen(pfds[0], "r"); 2529 2530 if (!rfp) { 2531 err = errno; 2532 close(pfds[0]); 2533 close(pfds[1]); 2534 err_return(err); 2535 } 2536 2537 wfp = fdopen(pfds[1], "w"); 2538 2539 if (!wfp) { 2540 err = errno; 2541 fclose(rfp); 2542 close(pfds[1]); 2543 err_return(err); 2544 } 2545 2546 rv = ucv_array_new_length(vm, 2); 2547 2548 ucv_array_push(rv, uc_resource_new(file_type, rfp)); 2549 ucv_array_push(rv, uc_resource_new(file_type, wfp)); 2550 2551 return rv; 2552 } 2553 2554 2555 static const uc_function_list_t proc_fns[] = { 2556 { "read", uc_fs_pread }, 2557 { "write", uc_fs_pwrite }, 2558 { "close", uc_fs_pclose }, 2559 { "flush", uc_fs_pflush }, 2560 { "fileno", uc_fs_pfileno }, 2561 { "error", uc_fs_error }, 2562 }; 2563 2564 static const uc_function_list_t file_fns[] = { 2565 { "read", uc_fs_read }, 2566 { "write", uc_fs_write }, 2567 { "seek", uc_fs_seek }, 2568 { "tell", uc_fs_tell }, 2569 { "close", uc_fs_close }, 2570 { "flush", uc_fs_flush }, 2571 { "fileno", uc_fs_fileno }, 2572 { "error", uc_fs_error }, 2573 { "isatty", uc_fs_isatty }, 2574 }; 2575 2576 static const uc_function_list_t dir_fns[] = { 2577 { "read", uc_fs_readdir }, 2578 { "seek", uc_fs_seekdir }, 2579 { "tell", uc_fs_telldir }, 2580 { "close", uc_fs_closedir }, 2581 { "error", uc_fs_error }, 2582 }; 2583 2584 static const uc_function_list_t global_fns[] = { 2585 { "error", uc_fs_error }, 2586 { "open", uc_fs_open }, 2587 { "fdopen", uc_fs_fdopen }, 2588 { "opendir", uc_fs_opendir }, 2589 { "popen", uc_fs_popen }, 2590 { "readlink", uc_fs_readlink }, 2591 { "stat", uc_fs_stat }, 2592 { "lstat", uc_fs_lstat }, 2593 { "mkdir", uc_fs_mkdir }, 2594 { "rmdir", uc_fs_rmdir }, 2595 { "symlink", uc_fs_symlink }, 2596 { "unlink", uc_fs_unlink }, 2597 { "getcwd", uc_fs_getcwd }, 2598 { "chdir", uc_fs_chdir }, 2599 { "chmod", uc_fs_chmod }, 2600 { "chown", uc_fs_chown }, 2601 { "rename", uc_fs_rename }, 2602 { "glob", uc_fs_glob }, 2603 { "dirname", uc_fs_dirname }, 2604 { "basename", uc_fs_basename }, 2605 { "lsdir", uc_fs_lsdir }, 2606 { "mkstemp", uc_fs_mkstemp }, 2607 { "access", uc_fs_access }, 2608 { "readfile", uc_fs_readfile }, 2609 { "writefile", uc_fs_writefile }, 2610 { "realpath", uc_fs_realpath }, 2611 { "pipe", uc_fs_pipe }, 2612 }; 2613 2614 2615 static void close_proc(void *ud) 2616 { 2617 FILE *fp = ud; 2618 2619 if (fp) 2620 pclose(fp); 2621 } 2622 2623 static void close_file(void *ud) 2624 { 2625 FILE *fp = ud; 2626 int n; 2627 2628 n = fp ? fileno(fp) : -1; 2629 2630 if (n > 2) 2631 fclose(fp); 2632 } 2633 2634 static void close_dir(void *ud) 2635 { 2636 DIR *dp = ud; 2637 2638 if (dp) 2639 closedir(dp); 2640 } 2641 2642 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 2643 { 2644 uc_function_list_register(scope, global_fns); 2645 2646 proc_type = uc_type_declare(vm, "fs.proc", proc_fns, close_proc); 2647 file_type = uc_type_declare(vm, "fs.file", file_fns, close_file); 2648 dir_type = uc_type_declare(vm, "fs.dir", dir_fns, close_dir); 2649 2650 ucv_object_add(scope, "stdin", uc_resource_new(file_type, stdin)); 2651 ucv_object_add(scope, "stdout", uc_resource_new(file_type, stdout)); 2652 ucv_object_add(scope, "stderr", uc_resource_new(file_type, stderr)); 2653 } 2654
This page was automatically generated by LXR 0.3.1. • OpenWrt