@scriptc/cli-darwin-arm64 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/dist/THIRD_PARTY_NOTICES +1 -1
  2. package/dist/bin/scriptc +0 -0
  3. package/dist/bin/scriptc.json +1 -1
  4. package/dist/lib/declarations/scriptc-node-fallback.d.ts +99 -27
  5. package/dist/lib/llvm/bin/scriptc-llvm-codegen +0 -0
  6. package/dist/lib/llvm/package.json +1 -1
  7. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_array.o +0 -0
  8. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_assert.o +0 -0
  9. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_async.o +0 -0
  10. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_bytes_io.o +0 -0
  11. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_events.o +0 -0
  12. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_exception.o +0 -0
  13. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_file_handle.o +0 -0
  14. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_json.o +0 -0
  15. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_lib.o +0 -0
  16. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_map.o +0 -0
  17. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_node_builtin.o +0 -0
  18. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_regex.o +0 -0
  19. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_symbol.o +0 -0
  20. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_url.o +0 -0
  21. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_url_params.o +0 -0
  22. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_util_compare.o +0 -0
  23. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_util_style.o +0 -0
  24. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_array.o +0 -0
  25. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_assert.o +0 -0
  26. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_async.o +0 -0
  27. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_bytes_io.o +0 -0
  28. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_events.o +0 -0
  29. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_exception.o +0 -0
  30. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_file_handle.o +0 -0
  31. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_island.o +0 -0
  32. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_json.o +0 -0
  33. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_lib.o +0 -0
  34. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_map.o +0 -0
  35. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_node_builtin.o +0 -0
  36. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_regex.o +0 -0
  37. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_symbol.o +0 -0
  38. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_url.o +0 -0
  39. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_url_params.o +0 -0
  40. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_util_compare.o +0 -0
  41. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_util_style.o +0 -0
  42. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_web.o +0 -0
  43. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_array.o +0 -0
  44. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_assert.o +0 -0
  45. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_bytes_io.o +0 -0
  46. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_exception.o +0 -0
  47. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_json.o +0 -0
  48. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_lib.o +0 -0
  49. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_map.o +0 -0
  50. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_node_builtin.o +0 -0
  51. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_regex.o +0 -0
  52. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_symbol.o +0 -0
  53. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_url.o +0 -0
  54. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_url_params.o +0 -0
  55. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_array.o +0 -0
  56. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_assert.o +0 -0
  57. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_bytes_io.o +0 -0
  58. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_exception.o +0 -0
  59. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_json.o +0 -0
  60. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_lib.o +0 -0
  61. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_map.o +0 -0
  62. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_node_builtin.o +0 -0
  63. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_regex.o +0 -0
  64. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_symbol.o +0 -0
  65. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_url.o +0 -0
  66. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_url_params.o +0 -0
  67. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_array.o +0 -0
  68. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_assert.o +0 -0
  69. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_bytes_io.o +0 -0
  70. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_exception.o +0 -0
  71. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_json.o +0 -0
  72. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_lib.o +0 -0
  73. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_map.o +0 -0
  74. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_node_builtin.o +0 -0
  75. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_regex.o +0 -0
  76. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_symbol.o +0 -0
  77. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_url.o +0 -0
  78. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_url_params.o +0 -0
  79. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_array.o +0 -0
  80. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_assert.o +0 -0
  81. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_bytes_io.o +0 -0
  82. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_exception.o +0 -0
  83. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_json.o +0 -0
  84. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_lib.o +0 -0
  85. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_map.o +0 -0
  86. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_node_builtin.o +0 -0
  87. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_regex.o +0 -0
  88. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_symbol.o +0 -0
  89. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_url.o +0 -0
  90. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_url_params.o +0 -0
  91. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_array.o +0 -0
  92. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_assert.o +0 -0
  93. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_async.o +0 -0
  94. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_bytes_io.o +0 -0
  95. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_events.o +0 -0
  96. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_exception.o +0 -0
  97. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_file_handle.o +0 -0
  98. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_json.o +0 -0
  99. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_lib.o +0 -0
  100. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_map.o +0 -0
  101. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_node_builtin.o +0 -0
  102. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_regex.o +0 -0
  103. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_symbol.o +0 -0
  104. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_url.o +0 -0
  105. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_url_params.o +0 -0
  106. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_util_compare.o +0 -0
  107. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_util_style.o +0 -0
  108. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_array.o +0 -0
  109. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_assert.o +0 -0
  110. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_async.o +0 -0
  111. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_bytes_io.o +0 -0
  112. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_events.o +0 -0
  113. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_exception.o +0 -0
  114. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_file_handle.o +0 -0
  115. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_island.o +0 -0
  116. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_json.o +0 -0
  117. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_lib.o +0 -0
  118. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_map.o +0 -0
  119. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_node_builtin.o +0 -0
  120. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_regex.o +0 -0
  121. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_symbol.o +0 -0
  122. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_url.o +0 -0
  123. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_url_params.o +0 -0
  124. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_util_compare.o +0 -0
  125. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_util_style.o +0 -0
  126. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_web.o +0 -0
  127. package/dist/lib/runtime-darwin-arm64/package.json +1 -1
  128. package/dist/lib/runtime-darwin-arm64/runtime-pack.json +351 -225
  129. package/dist/lib/runtime-sources/package.json +1 -1
  130. package/dist/lib/runtime-sources/src/scr_array.c +5 -0
  131. package/dist/lib/runtime-sources/src/scr_assert.c +26 -0
  132. package/dist/lib/runtime-sources/src/scr_async.c +44 -0
  133. package/dist/lib/runtime-sources/src/scr_bytes_io.c +305 -48
  134. package/dist/lib/runtime-sources/src/scr_events.c +9 -0
  135. package/dist/lib/runtime-sources/src/scr_exception.c +1 -0
  136. package/dist/lib/runtime-sources/src/scr_file_handle.c +11 -0
  137. package/dist/lib/runtime-sources/src/scr_island.c +1001 -417
  138. package/dist/lib/runtime-sources/src/scr_island_manifest.h +11 -7
  139. package/dist/lib/runtime-sources/src/scr_json.c +135 -2
  140. package/dist/lib/runtime-sources/src/scr_lib.c +379 -12
  141. package/dist/lib/runtime-sources/src/scr_map.c +8 -0
  142. package/dist/lib/runtime-sources/src/scr_node_builtin.c +78 -0
  143. package/dist/lib/runtime-sources/src/scr_regex.c +65 -0
  144. package/dist/lib/runtime-sources/src/scr_runtime.h +69 -1
  145. package/dist/lib/runtime-sources/src/scr_symbol.c +5 -0
  146. package/dist/lib/runtime-sources/src/scr_system_errors.h +573 -0
  147. package/dist/lib/runtime-sources/src/scr_url.c +515 -59
  148. package/dist/lib/runtime-sources/src/scr_url_internal.h +6 -0
  149. package/dist/lib/runtime-sources/src/scr_url_params.c +10 -0
  150. package/dist/lib/runtime-sources/src/scr_util_compare.c +222 -0
  151. package/dist/lib/runtime-sources/src/scr_util_style.c +337 -0
  152. package/dist/lib/runtime-sources/src/scr_web.c +720 -238
  153. package/package.json +1 -1
@@ -27,6 +27,13 @@
27
27
  #include <stdlib.h>
28
28
  #include <string.h>
29
29
  #include <sys/stat.h>
30
+ #if defined(__linux__)
31
+ #include <sys/vfs.h>
32
+ #elif defined(__APPLE__) || defined(__FreeBSD__)
33
+ #include <sys/mount.h>
34
+ #elif !defined(_WIN32) && !defined(__wasi__)
35
+ #include <sys/statvfs.h>
36
+ #endif
30
37
  #include <time.h>
31
38
 
32
39
  #ifdef _WIN32
@@ -55,6 +62,8 @@
55
62
 
56
63
  static WCHAR *scr_fs_win_wide(const ScrStr *path);
57
64
  static int scr_fs_win_errno(DWORD error);
65
+ static bool scr_stats_is_link_tag(DWORD tag);
66
+ static bool scr_stats_link_target(HANDLE h, DWORD tag, double *size_out, ScrStr **text_out);
58
67
 
59
68
  static ScrStr *scr_fs_win_utf8(const WCHAR *text, size_t length) {
60
69
  if (length > INT_MAX) { SetLastError(ERROR_FILENAME_EXCED_RANGE); return NULL; }
@@ -869,10 +878,12 @@ ScrDyn *scr_util_parse_env(const ScrDyn *content) {
869
878
  return scr_env_parse_content(content->v.str, true);
870
879
  }
871
880
 
872
- static void scr_env_invalid_path(const ScrDyn *value, const ScrStr *path) {
881
+ void scr_fs_invalid_named_path(const ScrDyn *value, const ScrStr *path, const char *name) {
873
882
  ScrJsonBuf b;
874
883
  scr_jb_init(&b);
875
- scr_jb_puts(&b, "The argument 'path' must be a string, Uint8Array, or URL without null bytes. Received ");
884
+ scr_jb_puts(&b, "The argument '");
885
+ scr_jb_puts(&b, name);
886
+ scr_jb_puts(&b, "' must be a string, Uint8Array, or URL without null bytes. Received ");
876
887
  if (value->kind == SCR_DYN_BYTES) {
877
888
  const ScrBytes *bytes = value->v.bytes;
878
889
  if (value->buffer) {
@@ -916,6 +927,10 @@ static void scr_env_invalid_path(const ScrDyn *value, const ScrStr *path) {
916
927
  scr_str_release(message);
917
928
  }
918
929
 
930
+ void scr_fs_invalid_path(const ScrDyn *value, const ScrStr *path) {
931
+ scr_fs_invalid_named_path(value, path, "path");
932
+ }
933
+
919
934
  void scr_process_load_env_file(const ScrDyn *value) {
920
935
  ScrStr *path;
921
936
  if (value->kind == SCR_DYN_UNDEF || value->kind == SCR_DYN_NULL) path = scr_str_new("./.env", 6);
@@ -928,7 +943,7 @@ void scr_process_load_env_file(const ScrDyn *value) {
928
943
  }
929
944
  if (!path) return;
930
945
  if (memchr(path->data, 0, path->len)) {
931
- scr_env_invalid_path(value, path);
946
+ scr_fs_invalid_path(value, path);
932
947
  scr_str_release(path);
933
948
  return;
934
949
  }
@@ -2501,7 +2516,45 @@ void scr_fs_mkdir_mode(ScrStr *path, double mode) {
2501
2516
  if (scr_sys_mkdir(path->data, (mode_t)mode) != 0) scr_fs_throw(errno, "mkdir", path);
2502
2517
  }
2503
2518
 
2519
+ #ifdef _WIN32
2520
+ /* The CRT's stat follows junctions. Recognize real link reparse records
2521
+ * before unlink/rm dispatch so a tree walk never descends into their targets.
2522
+ * Return 1 for a removed link, 0 for an ordinary entry, -1 on failure. */
2523
+ static int scr_fs_win_remove_link(const char *path, size_t len) {
2524
+ ScrStr *text = scr_str_new(path, len);
2525
+ WCHAR *wide = scr_fs_win_wide(text);
2526
+ scr_str_release(text);
2527
+ if (!wide) { errno = scr_fs_win_errno(GetLastError()); return -1; }
2528
+ DWORD attributes = GetFileAttributesW(wide);
2529
+ if (attributes == INVALID_FILE_ATTRIBUTES || !(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { free(wide); return 0; }
2530
+ HANDLE h = CreateFileW(wide, FILE_READ_ATTRIBUTES,
2531
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
2532
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
2533
+ if (h == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); free(wide); errno = scr_fs_win_errno(error); return -1; }
2534
+ FILE_ATTRIBUTE_TAG_INFO tagged;
2535
+ if (!GetFileInformationByHandleEx(h, FileAttributeTagInfo, &tagged, sizeof tagged)) {
2536
+ DWORD error = GetLastError(); CloseHandle(h); free(wide); errno = scr_fs_win_errno(error); return -1;
2537
+ }
2538
+ double size;
2539
+ bool link = scr_stats_is_link_tag(tagged.ReparseTag);
2540
+ // Mount-point tags also represent mounted volumes. Only junction payloads
2541
+ // are links; keep the existing ordinary-directory behavior for volumes.
2542
+ if (link && tagged.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) link = scr_stats_link_target(h, tagged.ReparseTag, &size, NULL);
2543
+ CloseHandle(h);
2544
+ if (!link) { free(wide); return 0; }
2545
+ BOOL ok = attributes & FILE_ATTRIBUTE_DIRECTORY ? RemoveDirectoryW(wide) : DeleteFileW(wide);
2546
+ DWORD error = ok ? 0 : GetLastError();
2547
+ free(wide);
2548
+ if (!ok) { errno = scr_fs_win_errno(error); return -1; }
2549
+ return 1;
2550
+ }
2551
+ #endif
2552
+
2504
2553
  void scr_fs_unlink(ScrStr *path) {
2554
+ #ifdef _WIN32
2555
+ int link = scr_fs_win_remove_link(path->data, path->len);
2556
+ if (link != 0) { if (link < 0) scr_fs_throw(errno, "unlink", path); return; }
2557
+ #endif
2505
2558
  if (unlink(path->data) != 0) scr_fs_throw(errno, "unlink", path);
2506
2559
  }
2507
2560
 
@@ -2665,6 +2718,80 @@ void scr_fs_ftruncate(double fd, double length) {
2665
2718
  #endif
2666
2719
  }
2667
2720
 
2721
+ #ifdef _WIN32
2722
+ static bool scr_fs_set_times(HANDLE handle, double access, double modified) {
2723
+ FILETIME atime, mtime, now;
2724
+ if (isinf(access) || isinf(modified)) GetSystemTimeAsFileTime(&now);
2725
+ double values[2] = { access, modified };
2726
+ FILETIME *files[2] = { &atime, &mtime };
2727
+ for (size_t i = 0; i < 2; i++) {
2728
+ if (isnan(values[i])) { files[i] = NULL; continue; }
2729
+ if (isinf(values[i])) { *files[i] = now; continue; }
2730
+ double ticks = values[i] * 10000000.0 + 116444736000000000.0;
2731
+ if (ticks < -9223372036854775808.0 || ticks >= 9223372036854775808.0) {
2732
+ SetLastError(ERROR_INVALID_PARAMETER);
2733
+ return false;
2734
+ }
2735
+ int64_t time = (int64_t)ticks;
2736
+ files[i]->dwLowDateTime = (DWORD)(uint64_t)time;
2737
+ files[i]->dwHighDateTime = (DWORD)((uint64_t)time >> 32);
2738
+ }
2739
+ return SetFileTime(handle, NULL, files[0], files[1]) != 0;
2740
+ }
2741
+
2742
+ static DWORD scr_fs_set_path_times(WCHAR *path, double atime, double mtime, bool nofollow) {
2743
+ DWORD flags = FILE_FLAG_BACKUP_SEMANTICS | (nofollow ? FILE_FLAG_OPEN_REPARSE_POINT : 0);
2744
+ HANDLE handle = CreateFileW(path, FILE_WRITE_ATTRIBUTES,
2745
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flags, NULL);
2746
+ if (handle == INVALID_HANDLE_VALUE) return GetLastError();
2747
+ DWORD error = scr_fs_set_times(handle, atime, mtime) ? 0 : GetLastError();
2748
+ CloseHandle(handle);
2749
+ return error;
2750
+ }
2751
+ #else
2752
+ /* Match the pinned libuv conversion: microsecond truncation, normalized
2753
+ * negative fractions, and the special current/unchanged time sentinels. */
2754
+ static bool scr_fs_timespec(double time, struct timespec *out) {
2755
+ if (isinf(time)) { *out = (struct timespec){ 0, UTIME_NOW }; return true; }
2756
+ if (isnan(time)) { *out = (struct timespec){ 0, UTIME_OMIT }; return true; }
2757
+ if (time < -9223372036854775808.0 || time >= 9223372036854775808.0) return false;
2758
+ out->tv_sec = (time_t)time;
2759
+ out->tv_nsec = (long)((time - (double)out->tv_sec) * 1000000000.0);
2760
+ out->tv_nsec -= out->tv_nsec % 1000;
2761
+ if (out->tv_nsec < 0) { out->tv_nsec += 1000000000; out->tv_sec--; }
2762
+ return true;
2763
+ }
2764
+ #endif
2765
+
2766
+ void scr_fs_utimes(ScrStr *path, double atime, double mtime, bool nofollow) {
2767
+ const char *op = nofollow ? "lutime" : "utime";
2768
+ #ifdef _WIN32
2769
+ WCHAR *wide = scr_fs_win_wide(path);
2770
+ if (!wide) { scr_fs_throw(scr_fs_win_errno(GetLastError()), op, path); return; }
2771
+ DWORD error = scr_fs_set_path_times(wide, atime, mtime, nofollow);
2772
+ if (nofollow && (error == ERROR_SYMLINK_NOT_SUPPORTED || error == ERROR_NOT_A_REPARSE_POINT))
2773
+ error = scr_fs_set_path_times(wide, atime, mtime, false);
2774
+ free(wide);
2775
+ if (error) scr_fs_throw(scr_fs_win_errno(error), op, path);
2776
+ #else
2777
+ struct timespec times[2];
2778
+ if (!scr_fs_timespec(atime, &times[0]) || !scr_fs_timespec(mtime, &times[1])) { scr_fs_throw(EINVAL, op, path); return; }
2779
+ if (utimensat(AT_FDCWD, path->data, times, nofollow ? AT_SYMLINK_NOFOLLOW : 0) != 0) scr_fs_throw(errno, op, path);
2780
+ #endif
2781
+ }
2782
+
2783
+ void scr_fs_futimes(double fd, double atime, double mtime) {
2784
+ #ifdef _WIN32
2785
+ HANDLE handle = (HANDLE)_get_osfhandle((int)fd);
2786
+ if (handle == INVALID_HANDLE_VALUE) { scr_fs_throw_nopath(EBADF, "futime"); return; }
2787
+ if (!scr_fs_set_times(handle, atime, mtime)) scr_fs_throw_nopath(scr_fs_win_errno(GetLastError()), "futime");
2788
+ #else
2789
+ struct timespec times[2];
2790
+ if (!scr_fs_timespec(atime, &times[0]) || !scr_fs_timespec(mtime, &times[1])) { scr_fs_throw_nopath(EINVAL, "futime"); return; }
2791
+ if (futimens((int)fd, times) != 0) scr_fs_throw_nopath(errno, "futime");
2792
+ #endif
2793
+ }
2794
+
2668
2795
  /* fs.closeSync(fd) — close(2); failure throws Node's path-less fs error
2669
2796
  * shape ("EBADF: bad file descriptor, close"). */
2670
2797
 
@@ -3165,18 +3292,15 @@ void scr_fs_link(ScrStr *source, ScrStr *dest) {
3165
3292
  WCHAR *from = scr_fs_win_wide(source);
3166
3293
  WCHAR *to = scr_fs_win_wide(dest);
3167
3294
  if (!from || !to) {
3295
+ DWORD error = GetLastError();
3168
3296
  free(from); free(to);
3169
- scr_fs_throw2(EINVAL, "link", source, dest);
3297
+ scr_fs_throw2(scr_fs_win_errno(error), "link", source, dest);
3170
3298
  return;
3171
3299
  }
3172
3300
  BOOL ok = CreateHardLinkW(to, from, NULL);
3173
3301
  DWORD error = ok ? 0 : GetLastError();
3174
3302
  free(from); free(to);
3175
- if (!ok) {
3176
- int code = error == ERROR_ALREADY_EXISTS || error == ERROR_FILE_EXISTS ? EEXIST :
3177
- error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND ? ENOENT : EACCES;
3178
- scr_fs_throw2(code, "link", source, dest);
3179
- }
3303
+ if (!ok) scr_fs_throw2(scr_fs_win_errno(error), "link", source, dest);
3180
3304
  #else
3181
3305
  if (link(source->data, dest->data) != 0) scr_fs_throw2(errno, "link", source, dest);
3182
3306
  #endif
@@ -3347,6 +3471,10 @@ void scr_fs_rename(ScrStr *oldpath, ScrStr *newpath) {
3347
3471
  static int scr_rm_unlink(const char *path, size_t len);
3348
3472
 
3349
3473
  void scr_fs_rm(ScrStr *path) {
3474
+ #ifdef _WIN32
3475
+ int link = scr_fs_win_remove_link(path->data, path->len);
3476
+ if (link != 0) { if (link < 0) scr_fs_throw(errno, "unlink", path); return; }
3477
+ #endif
3350
3478
  /* Node's rmSync: lstat first (a missing path reports the lstat syscall),
3351
3479
  * refuse directories (Node requires `recursive`, which the scriptc
3352
3480
  * surface doesn't declare — the message wording diverges from Node's
@@ -3501,6 +3629,10 @@ static int scr_rm_unlink(const char *path, size_t len) {
3501
3629
  /* Post-order tree removal for rmSync's recursive form. Stops at (and
3502
3630
  * records) the first failure, with the failing path and syscall name. */
3503
3631
  static void scr_rm_tree_e(const char *path, size_t len, ScrRmFail *f) {
3632
+ #ifdef _WIN32
3633
+ int link = scr_fs_win_remove_link(path, len);
3634
+ if (link != 0) { if (link < 0) scr_rm_fail_set(f, errno, "unlink", path, len); return; }
3635
+ #endif
3504
3636
  struct stat st;
3505
3637
  if (lstat(path, &st) != 0) {
3506
3638
  scr_rm_fail_set(f, errno, "lstat", path, len);
@@ -3541,6 +3673,10 @@ static void scr_rm_tree_e(const char *path, size_t len, ScrRmFail *f) {
3541
3673
  * force's ENOENT swallow, the non-recursive directory rejection, and the
3542
3674
  * tree walk — failures recorded in `f`, never thrown. */
3543
3675
  static void scr_fs_rm_attempt(ScrStr *path, bool recursive, bool force, ScrRmFail *f) {
3676
+ #ifdef _WIN32
3677
+ int link = scr_fs_win_remove_link(path->data, path->len);
3678
+ if (link != 0) { if (link < 0) scr_rm_fail_set(f, errno, "unlink", path->data, path->len); return; }
3679
+ #endif
3544
3680
  struct stat st;
3545
3681
  if (lstat(path->data, &st) != 0) {
3546
3682
  if (force && errno == ENOENT) return; /* Node: force swallows ENOENT */
@@ -3994,7 +4130,7 @@ typedef struct {
3994
4130
  /* Node/libuv reports a Windows link's target-text length as st_size. The
3995
4131
  * ordinary handle information does not carry that value, so read the reparse
3996
4132
  * payload while the no-follow handle is live. */
3997
- static bool scr_stats_link_size(HANDLE h, DWORD tag, double *size_out) {
4133
+ static bool scr_stats_link_target(HANDLE h, DWORD tag, double *size_out, ScrStr **text_out) {
3998
4134
  union {
3999
4135
  ScrStatsReparseData align;
4000
4136
  unsigned char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
@@ -4039,6 +4175,7 @@ static bool scr_stats_link_size(HANDLE h, DWORD tag, double *size_out) {
4039
4175
  /* WSL's LX symlink payload is a version word followed by UTF-8 bytes. */
4040
4176
  if (data->data_len < sizeof(ULONG)) return false;
4041
4177
  *size_out = (double)(data->data_len - sizeof(ULONG));
4178
+ if (text_out) *text_out = scr_str_new((const char *)data->body.generic.bytes + sizeof(ULONG), data->data_len - sizeof(ULONG));
4042
4179
  return true;
4043
4180
  } else {
4044
4181
  /* App execution links carry a counted UTF-16 string list; the third
@@ -4067,6 +4204,10 @@ static bool scr_stats_link_size(HANDLE h, DWORD tag, double *size_out) {
4067
4204
  double size = scr_stats_utf16_len(target, len);
4068
4205
  if (size < 0) return false;
4069
4206
  *size_out = size;
4207
+ if (text_out) {
4208
+ *text_out = scr_fs_win_utf8(target, len);
4209
+ if (!*text_out) return false;
4210
+ }
4070
4211
  return true;
4071
4212
  }
4072
4213
 
@@ -4092,6 +4233,10 @@ static bool scr_stats_link_size(HANDLE h, DWORD tag, double *size_out) {
4092
4233
  double size = scr_stats_utf16_len(target, len);
4093
4234
  if (size < 0) return false;
4094
4235
  *size_out = size;
4236
+ if (text_out) {
4237
+ *text_out = scr_fs_win_utf8(target, len);
4238
+ if (!*text_out) return false;
4239
+ }
4095
4240
  return true;
4096
4241
  }
4097
4242
 
@@ -4105,8 +4250,21 @@ static ScrStats *scr_stats_of_path(const ScrStr *path, const char *op,
4105
4250
  HANDLE h = CreateFileW(wide, FILE_READ_ATTRIBUTES,
4106
4251
  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
4107
4252
  NULL, OPEN_EXISTING, flags, NULL);
4253
+ if (h == INVALID_HANDLE_VALUE) {
4254
+ DWORD error = GetLastError();
4255
+ DWORD attributes = GetFileAttributesW(wide);
4256
+ free(wide);
4257
+ /* CRT stat can report the link entry when its target cannot be opened,
4258
+ * hiding dangling links and Windows file/directory type mismatches.
4259
+ * libuv preserves the failed follow operation for reparse points. */
4260
+ if (!no_follow && attributes != INVALID_FILE_ATTRIBUTES &&
4261
+ (attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
4262
+ scr_fs_throw(scr_fs_win_errno(error), op, path);
4263
+ return NULL;
4264
+ }
4265
+ return scr_stats_crt_fallback(path, op);
4266
+ }
4108
4267
  free(wide);
4109
- if (h == INVALID_HANDLE_VALUE) return scr_stats_crt_fallback(path, op);
4110
4268
 
4111
4269
  DWORD file_type = GetFileType(h);
4112
4270
  if (file_type != FILE_TYPE_DISK) {
@@ -4130,7 +4288,7 @@ static ScrStats *scr_stats_of_path(const ScrStr *path, const char *op,
4130
4288
  CloseHandle(h);
4131
4289
  return scr_stats_of_path(path, op, false);
4132
4290
  }
4133
- if (!scr_stats_link_size(h, tagged.ReparseTag, &link_size)) {
4291
+ if (!scr_stats_link_target(h, tagged.ReparseTag, &link_size, NULL)) {
4134
4292
  /* A mount-point tag may name a mounted volume rather than a junction.
4135
4293
  * libuv treats those (and malformed/unsupported link payloads) as
4136
4294
  * ordinary reparse points and retries with the final component
@@ -4207,6 +4365,215 @@ static ScrStats *scr_stats_of(const struct stat *st) {
4207
4365
  }
4208
4366
  #endif
4209
4367
 
4368
+ /* Symbolic link targets are stored verbatim on POSIX. Windows junctions
4369
+ * instead carry an absolute NT path in a bounded mount-point reparse record. */
4370
+ #ifdef _WIN32
4371
+ static DWORD scr_fs_win_junction(const WCHAR *target, const WCHAR *destination) {
4372
+ size_t len = wcslen(target);
4373
+ /* libuv permits drive-absolute junctions, not UNC mount targets. */
4374
+ size_t skip = len >= 4 && !wcsncmp(target, L"\\\\?\\", 4) ? 4 : 0;
4375
+ const WCHAR *plain = target + skip;
4376
+ size_t chars = len - skip;
4377
+ if (chars < 3 || !((plain[0] >= L'A' && plain[0] <= L'Z') || (plain[0] >= L'a' && plain[0] <= L'z')) || plain[1] != L':' || plain[2] != L'\\') return ERROR_INVALID_PARAMETER;
4378
+ size_t fixed = offsetof(ScrStatsReparseData, body.mount.path);
4379
+ size_t total = fixed + (4 + chars + 1 + chars + 1) * sizeof(WCHAR);
4380
+ if (total > MAXIMUM_REPARSE_DATA_BUFFER_SIZE) return ERROR_FILENAME_EXCED_RANGE;
4381
+ ScrStatsReparseData *data = calloc(1, total);
4382
+ if (!data) return ERROR_NOT_ENOUGH_MEMORY;
4383
+ data->tag = IO_REPARSE_TAG_MOUNT_POINT;
4384
+ data->data_len = (USHORT)(total - offsetof(ScrStatsReparseData, body));
4385
+ data->body.mount.substitute_offset = 0;
4386
+ data->body.mount.substitute_len = (USHORT)((4 + chars) * sizeof(WCHAR));
4387
+ data->body.mount.print_offset = (USHORT)((4 + chars + 1) * sizeof(WCHAR));
4388
+ data->body.mount.print_len = (USHORT)(chars * sizeof(WCHAR));
4389
+ memcpy(data->body.mount.path, L"\\??\\", 4 * sizeof(WCHAR));
4390
+ memcpy(data->body.mount.path + 4, plain, chars * sizeof(WCHAR));
4391
+ memcpy(data->body.mount.path + 4 + chars + 1, plain, chars * sizeof(WCHAR));
4392
+ DWORD error = 0, used;
4393
+ bool created = CreateDirectoryW(destination, NULL) != 0;
4394
+ HANDLE h = INVALID_HANDLE_VALUE;
4395
+ if (!created) error = GetLastError();
4396
+ else {
4397
+ h = CreateFileW(destination, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
4398
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
4399
+ if (h == INVALID_HANDLE_VALUE) error = GetLastError();
4400
+ else if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, data, (DWORD)total, NULL, 0, &used, NULL)) error = GetLastError();
4401
+ }
4402
+ if (h != INVALID_HANDLE_VALUE) CloseHandle(h);
4403
+ if (error && created) RemoveDirectoryW(destination);
4404
+ free(data);
4405
+ return error;
4406
+ }
4407
+ #endif
4408
+
4409
+ #ifdef _WIN32
4410
+ /* libuv keeps this fallback for later calls once Windows rejects the flag. */
4411
+ static DWORD scr_fs_symlink_usermode_flag = 0x2; /* ALLOW_UNPRIVILEGED_CREATE */
4412
+ #endif
4413
+
4414
+ int scr_fs_symlink_infer(ScrStr *absolute) {
4415
+ #ifdef _WIN32
4416
+ WCHAR *probe = scr_fs_win_wide(absolute);
4417
+ DWORD attributes = probe ? GetFileAttributesW(probe) : INVALID_FILE_ATTRIBUTES;
4418
+ free(probe);
4419
+ return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
4420
+ #else
4421
+ (void)absolute;
4422
+ return 0;
4423
+ #endif
4424
+ }
4425
+
4426
+ void scr_fs_symlink(ScrStr *target, ScrStr *destination, int kind) {
4427
+ #ifdef _WIN32
4428
+ ScrArr *parts = scr_arr_new_ref(scr_str_retain_v, scr_str_release_v, NULL, 3);
4429
+ scr_arr_push_ref(parts, scr_str_retain(destination));
4430
+ scr_arr_push_ref(parts, scr_str_new("..", 2));
4431
+ scr_arr_push_ref(parts, scr_str_retain(target));
4432
+ ScrStr *absolute = scr_path_win32_resolve(parts);
4433
+ scr_arr_release(parts);
4434
+ if (kind == -1) kind = scr_fs_symlink_infer(absolute);
4435
+ ScrStr *stored;
4436
+ if (kind == 2) stored = scr_path_win32_to_namespaced_path(absolute);
4437
+ else if (scr_path_win32_is_absolute(target)) stored = scr_path_win32_to_namespaced_path(target);
4438
+ else {
4439
+ char *text = malloc(target->len ? target->len : 1);
4440
+ if (!text) scr_trap("scriptc: out of memory\n");
4441
+ for (size_t i = 0; i < target->len; i++) text[i] = target->data[i] == '/' ? '\\' : target->data[i];
4442
+ stored = scr_str_new(text, target->len);
4443
+ free(text);
4444
+ }
4445
+ scr_str_release(absolute);
4446
+ /* Node's binding namespaces the link path before passing it to libuv. */
4447
+ ScrStr *link_path = scr_path_win32_to_namespaced_path(destination);
4448
+ WCHAR *from = scr_fs_win_wide(stored), *to = scr_fs_win_wide(link_path);
4449
+ DWORD error = !from || !to ? GetLastError() : 0;
4450
+ if (!error && kind == 2) error = scr_fs_win_junction(from, to);
4451
+ else if (!error) {
4452
+ DWORD flags = (kind == 1 ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) | scr_fs_symlink_usermode_flag;
4453
+ if (!CreateSymbolicLinkW(to, from, flags)) {
4454
+ error = GetLastError();
4455
+ if (error == ERROR_INVALID_PARAMETER && scr_fs_symlink_usermode_flag) {
4456
+ scr_fs_symlink_usermode_flag = 0;
4457
+ error = CreateSymbolicLinkW(to, from, flags & ~0x2u) ? 0 : GetLastError();
4458
+ }
4459
+ }
4460
+ }
4461
+ free(from); free(to); scr_str_release(stored); scr_str_release(link_path);
4462
+ if (error) scr_fs_throw2(scr_fs_win_errno(error), "symlink", target, destination);
4463
+ #else
4464
+ (void)kind;
4465
+ if (symlink(target->data, destination->data) != 0) scr_fs_throw2(errno, "symlink", target, destination);
4466
+ #endif
4467
+ }
4468
+
4469
+ /* Keep the unsigned libuv statfs representation before selecting Number or
4470
+ * BigInt. Free-space counts are live snapshots, not cached volume metadata. */
4471
+ ScrDyn *scr_fs_statfs(ScrStr *path, bool bigint) {
4472
+ uint64_t fields[7];
4473
+ #if defined(_WIN32)
4474
+ ScrStr *syscall_path = scr_path_win32_to_namespaced_path(path);
4475
+ WCHAR *wide = scr_fs_win_wide(syscall_path);
4476
+ scr_str_release(syscall_path);
4477
+ if (!wide) { scr_fs_throw(scr_fs_win_errno(GetLastError()), "statfs", path); return NULL; }
4478
+ DWORD sectors, bytes, free_clusters, total_clusters;
4479
+ BOOL ok = GetDiskFreeSpaceW(wide, &sectors, &bytes, &free_clusters, &total_clusters);
4480
+ DWORD error = ok ? 0 : GetLastError();
4481
+ if (!ok && error == ERROR_DIRECTORY) {
4482
+ /* libuv retries a file path's parent once, preserving the original path
4483
+ * in errors. GetFullPathName also expands relative and drive paths. */
4484
+ DWORD size = GetFullPathNameW(wide, 0, NULL, NULL);
4485
+ WCHAR *absolute = size ? malloc((size_t)size * sizeof *absolute) : NULL;
4486
+ if (size && !absolute) scr_trap("scriptc: out of memory\n");
4487
+ WCHAR *part = NULL;
4488
+ DWORD length = absolute ? GetFullPathNameW(wide, size, absolute, &part) : 0;
4489
+ if (length && length < size) {
4490
+ if (part) *part = L'\0';
4491
+ ok = GetDiskFreeSpaceW(absolute, &sectors, &bytes, &free_clusters, &total_clusters);
4492
+ if (!ok) error = GetLastError();
4493
+ }
4494
+ free(absolute);
4495
+ }
4496
+ free(wide);
4497
+ if (!ok) { scr_fs_throw(scr_fs_win_errno(error), "statfs", path); return NULL; }
4498
+ fields[0] = 0;
4499
+ fields[1] = (DWORD)(bytes * sectors);
4500
+ fields[2] = total_clusters;
4501
+ fields[3] = fields[4] = free_clusters;
4502
+ fields[5] = fields[6] = 0;
4503
+ #elif defined(__wasi__)
4504
+ (void)path; (void)bigint;
4505
+ static const char message[] = "Filesystem capacity queries have no native WASI syscall";
4506
+ scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020");
4507
+ return NULL;
4508
+ #else
4509
+ #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
4510
+ struct statfs snapshot;
4511
+ if (statfs(path->data, &snapshot) != 0) { scr_fs_throw(errno, "statfs", path); return NULL; }
4512
+ fields[0] = (uint64_t)snapshot.f_type;
4513
+ #else
4514
+ struct statvfs snapshot;
4515
+ if (statvfs(path->data, &snapshot) != 0) { scr_fs_throw(errno, "statfs", path); return NULL; }
4516
+ fields[0] = 0;
4517
+ #endif
4518
+ fields[1] = (uint64_t)snapshot.f_bsize;
4519
+ fields[2] = (uint64_t)snapshot.f_blocks;
4520
+ fields[3] = (uint64_t)snapshot.f_bfree;
4521
+ fields[4] = (uint64_t)snapshot.f_bavail;
4522
+ fields[5] = (uint64_t)snapshot.f_files;
4523
+ fields[6] = (uint64_t)snapshot.f_ffree;
4524
+ #endif
4525
+ static const char *names[] = { "type", "bsize", "blocks", "bfree", "bavail", "files", "ffree" };
4526
+ ScrDyn *result = scr_dyn_new_obj();
4527
+ for (size_t i = 0; i < 7; i++) {
4528
+ ScrDyn *value;
4529
+ if (bigint) {
4530
+ ScrBigInt *number = scr_bigint_from_u64(fields[i]);
4531
+ value = scr_dyn_new_bigint(number);
4532
+ scr_bigint_release(number);
4533
+ } else value = scr_dyn_new_num((double)fields[i]);
4534
+ scr_dyn_obj_set(result, names[i], strlen(names[i]), value);
4535
+ }
4536
+ return result;
4537
+ }
4538
+
4539
+ ScrBytes *scr_fs_readlink_bytes(ScrStr *path) {
4540
+ #ifdef _WIN32
4541
+ WCHAR *wide = scr_fs_win_wide(path);
4542
+ if (!wide) { scr_fs_throw(scr_fs_win_errno(GetLastError()), "readlink", path); return NULL; }
4543
+ HANDLE h = CreateFileW(wide, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
4544
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
4545
+ free(wide);
4546
+ if (h == INVALID_HANDLE_VALUE) { scr_fs_throw(scr_fs_win_errno(GetLastError()), "readlink", path); return NULL; }
4547
+ FILE_ATTRIBUTE_TAG_INFO tagged;
4548
+ ScrStr *text = NULL;
4549
+ double size;
4550
+ bool ok = GetFileInformationByHandleEx(h, FileAttributeTagInfo, &tagged, sizeof tagged) &&
4551
+ scr_stats_link_target(h, tagged.ReparseTag, &size, &text);
4552
+ CloseHandle(h);
4553
+ if (!ok || !text) { scr_str_release(text); scr_fs_throw(EINVAL, "readlink", path); return NULL; }
4554
+ ScrBytes *bytes = scr_bytes_from_data((const uint8_t *)text->data, text->len);
4555
+ scr_str_release(text);
4556
+ return bytes;
4557
+ #else
4558
+ size_t capacity = 256;
4559
+ uint8_t *text = NULL;
4560
+ for (;;) {
4561
+ uint8_t *grown = realloc(text, capacity);
4562
+ if (!grown) { free(text); scr_trap("scriptc: out of memory\n"); }
4563
+ text = grown;
4564
+ ssize_t size = readlink(path->data, (char *)text, capacity);
4565
+ if (size < 0) { int error = errno; free(text); scr_fs_throw(error, "readlink", path); return NULL; }
4566
+ if ((size_t)size < capacity) {
4567
+ ScrBytes *bytes = scr_bytes_from_data(text, (size_t)size);
4568
+ free(text);
4569
+ return bytes;
4570
+ }
4571
+ if (capacity > SIZE_MAX / 2) { free(text); scr_fs_throw(ENAMETOOLONG, "readlink", path); return NULL; }
4572
+ capacity *= 2;
4573
+ }
4574
+ #endif
4575
+ }
4576
+
4210
4577
  ScrStats *scr_fs_stat(ScrStr *path) {
4211
4578
  #ifdef _WIN32
4212
4579
  return scr_stats_of_path(path, "stat", false);
@@ -649,6 +649,14 @@ void scr_map_dyn_set(ScrMap *map, const ScrDyn *key, const ScrDyn *value, bool s
649
649
  scr_map_release_key(map, key_slot);
650
650
  }
651
651
 
652
+ ScrArr *scr_set_to_arr_dyn(const ScrMap *map) {
653
+ ScrArr *out = scr_arr_new_ref(scr_dyn_retain_v, scr_dyn_release_v, scr_dyn_trace_v, map->nlive);
654
+ for (size_t i = 0; i < map->nentries; i++) {
655
+ if (scr_map_iter_live(map, (double)i)) scr_arr_push_ref(out, scr_map_dyn_key(map, (double)i));
656
+ }
657
+ return out;
658
+ }
659
+
652
660
  void *scr_map_iter_val_ref(const ScrMap *m, double i) {
653
661
  return m->val_retain(scr_map_slot_to_ptr(scr_map_iter_at(m, i)->val)); /* +1 */
654
662
  }
@@ -1,11 +1,89 @@
1
1
  /* Native builtin export objects. The compiler supplies the existing static
2
2
  * callable implementations; this unit owns lookup, identity, and mutations. */
3
3
  #include "scr_runtime.h"
4
+ #include "scr_system_errors.h"
4
5
 
5
6
  #include <stdlib.h>
6
7
  #include <string.h>
7
8
  #include <math.h>
8
9
 
10
+ typedef struct { int value; const char *name; const char *message; } ScrSystemError;
11
+ #define SCR_SYSTEM_ERROR(name, message) { SCR_NODE_UV__##name, #name, message },
12
+ static const ScrSystemError scr_system_errors[] = {
13
+ SCR_NODE_UV_ERRNO_MAP(SCR_SYSTEM_ERROR)
14
+ };
15
+ #undef SCR_SYSTEM_ERROR
16
+
17
+ static bool scr_system_error_number(const ScrDyn *error, double *number) {
18
+ if (!error || error->kind != SCR_DYN_NUM) {
19
+ scr_dyn_arg_type_fail("err", "of type number", error ? error : scr_dyn_undefined());
20
+ return false;
21
+ }
22
+ double n = error->v.num;
23
+ if (!(isfinite(n) && n < 0 && trunc(n) == n && n >= -9007199254740991.0)) {
24
+ char received[48];
25
+ scr_num_received(n, received);
26
+ if (n == 0 && signbit(n)) strcpy(received, "-0");
27
+ ScrJsonBuf buffer;
28
+ scr_jb_init(&buffer);
29
+ scr_jb_puts(&buffer, "The value of \"err\" is out of range. It must be a negative integer. Received ");
30
+ scr_jb_puts(&buffer, received);
31
+ ScrStr *message = scr_jb_finish(&buffer);
32
+ scr_throw_error_msg_code(SCR_ERR_RANGE, message->data, message->len, "ERR_OUT_OF_RANGE");
33
+ scr_str_release(message);
34
+ return false;
35
+ }
36
+ *number = n;
37
+ return true;
38
+ }
39
+
40
+ static ScrStr *scr_system_error_lookup(double number, bool message) {
41
+ /* Node's name helper looks up the original safe integer. Its message
42
+ * binding uses V8's saturating Int32 extraction instead. */
43
+ double lookup = message && number < -2147483648.0 ? -2147483648.0 : number;
44
+ for (size_t i = 0; i < sizeof scr_system_errors / sizeof scr_system_errors[0]; i++) {
45
+ if (lookup == scr_system_errors[i].value) {
46
+ const char *text = message ? scr_system_errors[i].message : scr_system_errors[i].name;
47
+ return scr_str_new(text, strlen(text));
48
+ }
49
+ }
50
+ ScrJsonBuf buffer;
51
+ scr_jb_init(&buffer);
52
+ scr_jb_puts(&buffer, "Unknown system error ");
53
+ ScrStr *rendered = scr_f64_to_scrstr(lookup);
54
+ scr_jb_put_str(&buffer, rendered);
55
+ scr_str_release(rendered);
56
+ return scr_jb_finish(&buffer);
57
+ }
58
+
59
+ ScrStr *scr_util_system_error_name(const ScrDyn *error) {
60
+ double number;
61
+ return scr_system_error_number(error, &number) ? scr_system_error_lookup(number, false) : NULL;
62
+ }
63
+
64
+ ScrStr *scr_util_system_error_message(const ScrDyn *error) {
65
+ double number;
66
+ return scr_system_error_number(error, &number) ? scr_system_error_lookup(number, true) : NULL;
67
+ }
68
+
69
+ ScrDyn *scr_util_system_error_entries(void) {
70
+ ScrDyn *entries = scr_dyn_new_arr();
71
+ for (size_t i = 0; i < sizeof scr_system_errors / sizeof scr_system_errors[0]; i++) {
72
+ const ScrSystemError *error = &scr_system_errors[i];
73
+ ScrDyn *entry = scr_dyn_new_arr(), *pair = scr_dyn_new_arr();
74
+ ScrStr *name = scr_str_new(error->name, strlen(error->name));
75
+ ScrStr *message = scr_str_new(error->message, strlen(error->message));
76
+ scr_dyn_arr_push(pair, scr_dyn_new_str(name));
77
+ scr_dyn_arr_push(pair, scr_dyn_new_str(message));
78
+ scr_str_release(name);
79
+ scr_str_release(message);
80
+ scr_dyn_arr_push(entry, scr_dyn_new_num(error->value));
81
+ scr_dyn_arr_push(entry, pair);
82
+ scr_dyn_arr_push(entries, entry);
83
+ }
84
+ return entries;
85
+ }
86
+
9
87
  static SCR_TL ScrDyn *scr_global_known;
10
88
  static SCR_TL ScrDyn *scr_global_own;
11
89
  static SCR_TL bool scr_global_cleanup_registered;