xbintsc 0.3.12 → 0.3.14

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 (76) hide show
  1. package/README.md +21 -3
  2. package/README.zh-CN.md +18 -3
  3. package/dist/src/codegen/generator/calls/access.js +13 -0
  4. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  5. package/dist/src/codegen/generator/calls/invocation.js +8 -2
  6. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  7. package/dist/src/codegen/generator/context.d.ts +23 -0
  8. package/dist/src/codegen/generator/context.js +91 -17
  9. package/dist/src/codegen/generator/context.js.map +1 -1
  10. package/dist/src/codegen/generator/expressions/primary.js +5 -0
  11. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  12. package/dist/src/driver/compiler.d.ts +7 -0
  13. package/dist/src/driver/compiler.js +41 -5
  14. package/dist/src/driver/compiler.js.map +1 -1
  15. package/dist/src/driver/modules.d.ts +1 -1
  16. package/dist/src/driver/modules.js +235 -25
  17. package/dist/src/driver/modules.js.map +1 -1
  18. package/dist/src/extensions/node/fs/fs-ops.d.ts +5 -2
  19. package/dist/src/extensions/node/fs/fs-ops.js +45 -6
  20. package/dist/src/extensions/node/fs/fs-ops.js.map +1 -1
  21. package/dist/src/extensions/node/fs/index.d.ts +6 -2
  22. package/dist/src/extensions/node/fs/index.js +22 -3
  23. package/dist/src/extensions/node/fs/index.js.map +1 -1
  24. package/dist/src/extensions/node/fs-promises/index.d.ts +4 -3
  25. package/dist/src/extensions/node/fs-promises/index.js +31 -3
  26. package/dist/src/extensions/node/fs-promises/index.js.map +1 -1
  27. package/dist/tests/cli/main.test.js +17 -0
  28. package/dist/tests/cli/main.test.js.map +1 -1
  29. package/dist/tests/driver/compiler.test.js +93 -2
  30. package/dist/tests/driver/compiler.test.js.map +1 -1
  31. package/dist/tests/driver/modules.test.js +91 -4
  32. package/dist/tests/driver/modules.test.js.map +1 -1
  33. package/dist/tests/e2e/compile-node.test.js +240 -0
  34. package/dist/tests/e2e/compile-node.test.js.map +1 -1
  35. package/dist/tests/e2e/compile.test.js +12 -0
  36. package/dist/tests/e2e/compile.test.js.map +1 -1
  37. package/dist/tests/e2e/harness.js +11 -8
  38. package/dist/tests/e2e/harness.js.map +1 -1
  39. package/dist/tests/e2e/node-modules.test.js +1 -1
  40. package/dist/tests/e2e/node-modules.test.js.map +1 -1
  41. package/dist/tests/e2e/npm-modules.test.d.ts +9 -0
  42. package/dist/tests/e2e/npm-modules.test.js +55 -0
  43. package/dist/tests/e2e/npm-modules.test.js.map +1 -0
  44. package/dist/tests/e2e/runtime-coverage.test.d.ts +11 -0
  45. package/dist/tests/e2e/runtime-coverage.test.js +646 -0
  46. package/dist/tests/e2e/runtime-coverage.test.js.map +1 -0
  47. package/package.json +3 -2
  48. package/runtime/ext_node/buffer/parts/prototype.inc +7 -0
  49. package/runtime/ext_node/buffer/parts/search.inc +8 -4
  50. package/runtime/ext_node/child_process/child_process.c +5 -0
  51. package/runtime/ext_node/fs/constants.c +113 -0
  52. package/runtime/ext_node/fs/copy_ops.c +145 -0
  53. package/runtime/ext_node/fs/dir.c +101 -0
  54. package/runtime/ext_node/fs/fd_ops.c +405 -0
  55. package/runtime/ext_node/fs/fs_common.h +388 -5
  56. package/runtime/ext_node/fs/fs_ops.c +100 -139
  57. package/runtime/ext_node/fs/glob.c +232 -0
  58. package/runtime/ext_node/fs/link_ops.c +91 -0
  59. package/runtime/ext_node/fs/meta_ops.c +403 -0
  60. package/runtime/ext_node/fs/promises.c +275 -60
  61. package/runtime/ext_node/fs/read_file.c +8 -4
  62. package/runtime/ext_node/fs/watch.c +52 -0
  63. package/runtime/ext_node/fs/write_file.c +20 -11
  64. package/runtime/ext_node/stream/stream.c +19 -2
  65. package/runtime/rt.h +9 -0
  66. package/runtime/xt_io.c +21 -0
  67. package/scripts/coverage-runtime.ts +338 -0
  68. package/src/codegen/generator/calls/access.ts +15 -0
  69. package/src/codegen/generator/calls/invocation.ts +8 -2
  70. package/src/codegen/generator/context.ts +112 -21
  71. package/src/codegen/generator/expressions/primary.ts +5 -0
  72. package/src/driver/compiler.ts +42 -5
  73. package/src/driver/modules.ts +251 -26
  74. package/src/extensions/node/fs/fs-ops.ts +48 -6
  75. package/src/extensions/node/fs/index.ts +25 -3
  76. package/src/extensions/node/fs-promises/index.ts +33 -4
@@ -0,0 +1,403 @@
1
+ /*
2
+ * Node.js `fs` metadata / temporary-directory operations for xbintsc.
3
+ *
4
+ * accessSync, chmodSync, chownSync, truncateSync, utimesSync, lutimesSync,
5
+ * statfsSync, mkdtempSync
6
+ *
7
+ * Errors throw Node-shaped `Error` objects (see `fs_common.h`).
8
+ */
9
+
10
+ #include "../node_common.h"
11
+ #include "fs_common.h"
12
+
13
+ #include <sys/stat.h>
14
+ #include <sys/types.h>
15
+ #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
16
+ #include <sys/mount.h>
17
+ #elif defined(__linux__)
18
+ #include <sys/vfs.h>
19
+ #endif
20
+
21
+ #if defined(_WIN32)
22
+ #include <io.h>
23
+ #include <windows.h>
24
+ #define xt_fs_chmod_fn _chmod
25
+ #define xt_fs_truncate_path(path, length) xt_fs_win_truncate(path, length)
26
+ #else
27
+ #include <sys/time.h>
28
+ #include <unistd.h>
29
+ #define xt_fs_chmod_fn chmod
30
+ #define xt_fs_truncate_path(path, length) truncate((path), (off_t)(length))
31
+ #endif
32
+
33
+ #if defined(_WIN32)
34
+ static int xt_fs_errno_from_win32(DWORD error) {
35
+ switch (error) {
36
+ case ERROR_FILE_NOT_FOUND:
37
+ case ERROR_PATH_NOT_FOUND:
38
+ case ERROR_INVALID_NAME:
39
+ case ERROR_BAD_PATHNAME:
40
+ case ERROR_BAD_NETPATH:
41
+ case ERROR_INVALID_DRIVE:
42
+ return ENOENT;
43
+ case ERROR_ACCESS_DENIED:
44
+ case ERROR_SHARING_VIOLATION:
45
+ case ERROR_LOCK_VIOLATION:
46
+ return EACCES;
47
+ case ERROR_DIRECTORY:
48
+ return ENOTDIR;
49
+ #ifdef ENAMETOOLONG
50
+ case ERROR_FILENAME_EXCED_RANGE:
51
+ return ENAMETOOLONG;
52
+ #endif
53
+ case ERROR_NOT_ENOUGH_MEMORY:
54
+ case ERROR_OUTOFMEMORY:
55
+ return ENOMEM;
56
+ default:
57
+ return EINVAL;
58
+ }
59
+ }
60
+
61
+ /* FILETIME (100 ns ticks since 1601-01-01 UTC) -> Unix seconds, in UTC.
62
+ * Unlike the CRT's `_stat` this never round-trips through the local time zone,
63
+ * so epoch-relative times stay representable regardless of the machine's
64
+ * timezone (matching libuv/Node). */
65
+ static long long xt_fs_filetime_to_seconds(const FILETIME *time) {
66
+ unsigned long long value = ((unsigned long long)time->dwHighDateTime << 32) | time->dwLowDateTime;
67
+ if (value == 0) return 0;
68
+ return ((long long)value - 116444736000000000LL) / 10000000LL;
69
+ }
70
+
71
+ static void xt_fs_win_fill_stat(xt_fs_stat_t *out, DWORD attributes, DWORD volume,
72
+ unsigned long long index, unsigned int links, long long size,
73
+ const FILETIME *creation, const FILETIME *access,
74
+ const FILETIME *write) {
75
+ memset(out, 0, sizeof(*out));
76
+ if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
77
+ out->st_mode = _S_IFDIR;
78
+ size = 0;
79
+ } else {
80
+ out->st_mode = _S_IFREG;
81
+ }
82
+ if (attributes & FILE_ATTRIBUTE_READONLY) {
83
+ out->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
84
+ } else {
85
+ out->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
86
+ ((_S_IREAD | _S_IWRITE) >> 6);
87
+ }
88
+ out->st_size = size;
89
+ out->st_dev = volume;
90
+ out->st_ino = index;
91
+ out->st_nlink = links ? links : 1;
92
+ out->st_ctime = xt_fs_filetime_to_seconds(creation);
93
+ out->st_atime = xt_fs_filetime_to_seconds(access);
94
+ out->st_mtime = xt_fs_filetime_to_seconds(write);
95
+ }
96
+
97
+ int xt_fs_win_stat(const char *path, xt_fs_stat_t *out) {
98
+ WIN32_FILE_ATTRIBUTE_DATA data;
99
+ if (!path || !GetFileAttributesExA(path, GetFileExInfoStandard, &data)) {
100
+ errno = xt_fs_errno_from_win32(GetLastError());
101
+ return -1;
102
+ }
103
+ long long size = ((long long)data.nFileSizeHigh << 32) | data.nFileSizeLow;
104
+ xt_fs_win_fill_stat(out, data.dwFileAttributes, 0, 0, 1, size, &data.ftCreationTime,
105
+ &data.ftLastAccessTime, &data.ftLastWriteTime);
106
+ /* Best effort: fill the volume serial, file index and link count like Node. */
107
+ HANDLE handle = CreateFileA(path, FILE_READ_ATTRIBUTES,
108
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
109
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
110
+ if (handle != INVALID_HANDLE_VALUE) {
111
+ BY_HANDLE_FILE_INFORMATION info;
112
+ if (GetFileInformationByHandle(handle, &info)) {
113
+ out->st_dev = info.dwVolumeSerialNumber;
114
+ out->st_ino = ((unsigned long long)info.nFileIndexHigh << 32) | info.nFileIndexLow;
115
+ out->st_nlink = info.nNumberOfLinks ? info.nNumberOfLinks : 1;
116
+ }
117
+ CloseHandle(handle);
118
+ }
119
+ return 0;
120
+ }
121
+
122
+ int xt_fs_win_lstat(const char *path, xt_fs_stat_t *out) { return xt_fs_win_stat(path, out); }
123
+
124
+ int xt_fs_win_fstat(int fd, xt_fs_stat_t *out) {
125
+ intptr_t os_handle = _get_osfhandle(fd);
126
+ if (os_handle == -1) {
127
+ errno = EBADF;
128
+ return -1;
129
+ }
130
+ BY_HANDLE_FILE_INFORMATION info;
131
+ if (GetFileInformationByHandle((HANDLE)os_handle, &info)) {
132
+ long long size = ((long long)info.nFileSizeHigh << 32) | info.nFileSizeLow;
133
+ xt_fs_win_fill_stat(out, info.dwFileAttributes, info.dwVolumeSerialNumber,
134
+ ((unsigned long long)info.nFileIndexHigh << 32) | info.nFileIndexLow,
135
+ info.nNumberOfLinks, size, &info.ftCreationTime, &info.ftLastAccessTime,
136
+ &info.ftLastWriteTime);
137
+ return 0;
138
+ }
139
+ DWORD type = GetFileType((HANDLE)os_handle);
140
+ if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE) {
141
+ memset(out, 0, sizeof(*out));
142
+ out->st_mode = (type == FILE_TYPE_PIPE ? _S_IFIFO : _S_IFCHR) | _S_IREAD | _S_IWRITE |
143
+ (_S_IREAD >> 3) | (_S_IWRITE >> 3) | (_S_IREAD >> 6) | (_S_IWRITE >> 6);
144
+ out->st_nlink = 1;
145
+ out->st_ino = (unsigned long long)(uintptr_t)os_handle;
146
+ return 0;
147
+ }
148
+ errno = xt_fs_errno_from_win32(GetLastError());
149
+ return -1;
150
+ }
151
+
152
+ static int xt_fs_win_truncate(const char *path, long long length) {
153
+ int fd = _open(path, _O_RDWR | _O_BINARY);
154
+ if (fd < 0) return -1;
155
+ int result = _chsize_s(fd, length);
156
+ _close(fd);
157
+ return result;
158
+ }
159
+
160
+ /* Unix seconds -> Windows FILETIME (100 ns ticks since 1601-01-01 UTC). The CRT
161
+ * `_utime`/`_utimbuf` path goes through local time, which is both lossy around
162
+ * DST boundaries and ABI-fragile across MSVC/MinGW; matching libuv's direct
163
+ * conversion keeps the result exact and identical to Node. */
164
+ static void xt_fs_unix_to_filetime(double seconds, FILETIME *out) {
165
+ long long ticks = (long long)(seconds * 10000000.0) + 116444736000000000LL;
166
+ unsigned long long value = (unsigned long long)ticks;
167
+ out->dwLowDateTime = (DWORD)value;
168
+ out->dwHighDateTime = (DWORD)(value >> 32);
169
+ }
170
+
171
+ static int xt_fs_set_file_times(HANDLE handle, double atime, double mtime) {
172
+ FILETIME access_time;
173
+ FILETIME modify_time;
174
+ FILETIME *access_ptr = NULL;
175
+ FILETIME *modify_ptr = NULL;
176
+ if (atime == atime) {
177
+ xt_fs_unix_to_filetime(atime, &access_time);
178
+ access_ptr = &access_time;
179
+ }
180
+ if (mtime == mtime) {
181
+ xt_fs_unix_to_filetime(mtime, &modify_time);
182
+ modify_ptr = &modify_time;
183
+ }
184
+ return SetFileTime(handle, NULL, access_ptr, modify_ptr) ? 0 : -1;
185
+ }
186
+ #endif
187
+
188
+ /* ------------------------------------------------------------------------- */
189
+ /* accessSync */
190
+ /* ------------------------------------------------------------------------- */
191
+
192
+ xt_value xt_node_access(int32_t argc, xt_value *argv) {
193
+ if (argc < 1) return xt_undefined();
194
+ const char *path = xt_node_cstr(argv[0]);
195
+ int mode = argc > 1 ? (int)xt_to_number(argv[1]) : F_OK;
196
+ if (!path || xt_fs_access(path, mode) != 0) {
197
+ xt_fs_raise_path("access", path);
198
+ return xt_undefined();
199
+ }
200
+ return xt_undefined();
201
+ }
202
+
203
+ /* ------------------------------------------------------------------------- */
204
+ /* chmodSync / chownSync */
205
+ /* ------------------------------------------------------------------------- */
206
+
207
+ xt_value xt_node_chmod(int32_t argc, xt_value *argv) {
208
+ if (argc < 2) return xt_undefined();
209
+ const char *path = xt_node_cstr(argv[0]);
210
+ int mode = (int)xt_to_number(argv[1]);
211
+ if (!path || xt_fs_chmod_fn(path, mode) != 0) {
212
+ xt_fs_raise_path("chmod", path);
213
+ return xt_undefined();
214
+ }
215
+ return xt_undefined();
216
+ }
217
+
218
+ xt_value xt_node_chown(int32_t argc, xt_value *argv) {
219
+ if (argc < 3) return xt_undefined();
220
+ const char *path = xt_node_cstr(argv[0]);
221
+ int uid = (int)xt_to_number(argv[1]);
222
+ int gid = (int)xt_to_number(argv[2]);
223
+ #if defined(_WIN32)
224
+ (void)path;
225
+ (void)uid;
226
+ (void)gid;
227
+ return xt_undefined();
228
+ #else
229
+ if (!path || chown(path, (uid_t)uid, (gid_t)gid) != 0) {
230
+ xt_fs_raise_path("chown", path);
231
+ return xt_undefined();
232
+ }
233
+ return xt_undefined();
234
+ #endif
235
+ }
236
+
237
+ xt_value xt_node_lchown(int32_t argc, xt_value *argv) {
238
+ if (argc < 3) return xt_undefined();
239
+ const char *path = xt_node_cstr(argv[0]);
240
+ int uid = (int)xt_to_number(argv[1]);
241
+ int gid = (int)xt_to_number(argv[2]);
242
+ #if defined(_WIN32)
243
+ (void)path;
244
+ (void)uid;
245
+ (void)gid;
246
+ return xt_undefined();
247
+ #else
248
+ if (!path || lchown(path, (uid_t)uid, (gid_t)gid) != 0) {
249
+ xt_fs_raise_path("lchown", path);
250
+ return xt_undefined();
251
+ }
252
+ return xt_undefined();
253
+ #endif
254
+ }
255
+
256
+ /* Windows has no `lchmod`; Node ignores the call there. */
257
+ xt_value xt_node_lchmod(int32_t argc, xt_value *argv) {
258
+ #if defined(_WIN32)
259
+ (void)argc;
260
+ (void)argv;
261
+ return xt_undefined();
262
+ #else
263
+ return xt_node_chmod(argc, argv);
264
+ #endif
265
+ }
266
+
267
+ /* ------------------------------------------------------------------------- */
268
+ /* truncateSync */
269
+ /* ------------------------------------------------------------------------- */
270
+
271
+ xt_value xt_node_truncate(int32_t argc, xt_value *argv) {
272
+ if (argc < 1) return xt_undefined();
273
+ const char *path = xt_node_cstr(argv[0]);
274
+ long long length = argc > 1 ? (long long)xt_to_number(argv[1]) : 0;
275
+ if (!path || xt_fs_truncate_path(path, length) != 0) {
276
+ xt_fs_raise_path("truncate", path);
277
+ return xt_undefined();
278
+ }
279
+ return xt_undefined();
280
+ }
281
+
282
+ /* ------------------------------------------------------------------------- */
283
+ /* utimesSync / lutimesSync */
284
+ /* ------------------------------------------------------------------------- */
285
+
286
+ xt_value xt_node_utimes(int32_t argc, xt_value *argv) {
287
+ if (argc < 3) return xt_undefined();
288
+ const char *path = xt_node_cstr(argv[0]);
289
+ double atime = xt_fs_time_seconds(argv[1]);
290
+ double mtime = xt_fs_time_seconds(argv[2]);
291
+ #if defined(_WIN32)
292
+ HANDLE handle = path ? CreateFileA(path, FILE_WRITE_ATTRIBUTES,
293
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
294
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)
295
+ : INVALID_HANDLE_VALUE;
296
+ if (handle == INVALID_HANDLE_VALUE || xt_fs_set_file_times(handle, atime, mtime) != 0) {
297
+ if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle);
298
+ xt_fs_raise_path("utime", path);
299
+ return xt_undefined();
300
+ }
301
+ CloseHandle(handle);
302
+ return xt_undefined();
303
+ #else
304
+ struct timeval times[2];
305
+ times[0].tv_sec = (time_t)atime;
306
+ times[0].tv_usec = (suseconds_t)((atime - (double)times[0].tv_sec) * 1000000.0);
307
+ times[1].tv_sec = (time_t)mtime;
308
+ times[1].tv_usec = (suseconds_t)((mtime - (double)times[1].tv_sec) * 1000000.0);
309
+ if (!path || utimes(path, times) != 0) {
310
+ xt_fs_raise_path("utimes", path);
311
+ return xt_undefined();
312
+ }
313
+ return xt_undefined();
314
+ #endif
315
+ }
316
+
317
+ xt_value xt_node_lutimes(int32_t argc, xt_value *argv) {
318
+ #if defined(__linux__)
319
+ if (argc < 3) return xt_undefined();
320
+ const char *path = xt_node_cstr(argv[0]);
321
+ double atime = xt_fs_time_seconds(argv[1]);
322
+ double mtime = xt_fs_time_seconds(argv[2]);
323
+ struct timeval times[2];
324
+ times[0].tv_sec = (time_t)atime;
325
+ times[0].tv_usec = (suseconds_t)((atime - (double)times[0].tv_sec) * 1000000.0);
326
+ times[1].tv_sec = (time_t)mtime;
327
+ times[1].tv_usec = (suseconds_t)((mtime - (double)times[1].tv_sec) * 1000000.0);
328
+ if (!path || lutimes(path, times) != 0) {
329
+ xt_fs_raise_path("lutimes", path);
330
+ return xt_undefined();
331
+ }
332
+ return xt_undefined();
333
+ #else
334
+ /* macOS and Windows have no `lutimes`; fall back to `utimes`. */
335
+ return xt_node_utimes(argc, argv);
336
+ #endif
337
+ }
338
+
339
+ /* ------------------------------------------------------------------------- */
340
+ /* statfsSync */
341
+ /* ------------------------------------------------------------------------- */
342
+
343
+ xt_value xt_node_statfs(int32_t argc, xt_value *argv) {
344
+ xt_value object = xt_object_new();
345
+ #if defined(_WIN32) || defined(_AIX) || defined(__sun)
346
+ (void)argc;
347
+ (void)argv;
348
+ xt_object_set(object, xt_string_from_cstr("type"), xt_number(0));
349
+ xt_object_set(object, xt_string_from_cstr("bsize"), xt_number(0));
350
+ xt_object_set(object, xt_string_from_cstr("blocks"), xt_number(0));
351
+ xt_object_set(object, xt_string_from_cstr("bfree"), xt_number(0));
352
+ xt_object_set(object, xt_string_from_cstr("bavail"), xt_number(0));
353
+ xt_object_set(object, xt_string_from_cstr("files"), xt_number(0));
354
+ xt_object_set(object, xt_string_from_cstr("ffree"), xt_number(0));
355
+ #else
356
+ const char *path = argc > 0 ? xt_node_cstr(argv[0]) : NULL;
357
+ struct statfs info;
358
+ if (!path || statfs(path, &info) != 0) {
359
+ xt_fs_raise_path("statfs", path);
360
+ return xt_undefined();
361
+ }
362
+ xt_object_set(object, xt_string_from_cstr("type"), xt_number((double)info.f_type));
363
+ xt_object_set(object, xt_string_from_cstr("bsize"), xt_number((double)info.f_bsize));
364
+ xt_object_set(object, xt_string_from_cstr("blocks"), xt_number((double)info.f_blocks));
365
+ xt_object_set(object, xt_string_from_cstr("bfree"), xt_number((double)info.f_bfree));
366
+ xt_object_set(object, xt_string_from_cstr("bavail"), xt_number((double)info.f_bavail));
367
+ xt_object_set(object, xt_string_from_cstr("files"), xt_number((double)info.f_files));
368
+ xt_object_set(object, xt_string_from_cstr("ffree"), xt_number((double)info.f_ffree));
369
+ #endif
370
+ return object;
371
+ }
372
+
373
+ /* ------------------------------------------------------------------------- */
374
+ /* mkdtempSync */
375
+ /* ------------------------------------------------------------------------- */
376
+
377
+ xt_value xt_node_mkdtemp(int32_t argc, xt_value *argv) {
378
+ if (argc < 1) return xt_undefined();
379
+ const char *prefix = xt_node_cstr(argv[0]);
380
+ if (!prefix) {
381
+ xt_fs_raise_errno(EINVAL, "mkdtemp", NULL);
382
+ return xt_undefined();
383
+ }
384
+ size_t length = strlen(prefix);
385
+ char *dir = (char *)malloc(length + 7);
386
+ if (!dir) return xt_undefined();
387
+ memcpy(dir, prefix, length);
388
+ static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789";
389
+ for (int attempt = 0; attempt < 100; attempt++) {
390
+ unsigned int seed = (unsigned int)(time(NULL) ^ (attempt * 2654435761u));
391
+ for (int i = 0; i < 6; i++) dir[length + i] = alphabet[seed % 36u], seed = seed * 1103515245u + 12345u;
392
+ dir[length + 6] = '\0';
393
+ if (xt_fs_mkdir_one(dir) == 0) {
394
+ xt_value result = xt_string_from_cstr(dir);
395
+ free(dir);
396
+ return result;
397
+ }
398
+ if (errno != EEXIST) break;
399
+ }
400
+ free(dir);
401
+ xt_fs_raise_path("mkdtemp", prefix);
402
+ return xt_undefined();
403
+ }