xbintsc 0.5.20 → 0.5.21

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 (100) hide show
  1. package/AGENTS.md +5 -3
  2. package/dist/src/driver/compiler.js +1 -1
  3. package/dist/src/driver/compiler.js.map +1 -1
  4. package/dist/src/driver/runtime-objects.d.ts +1 -1
  5. package/dist/src/driver/runtime-objects.js +1 -0
  6. package/dist/src/driver/runtime-objects.js.map +1 -1
  7. package/dist/src/driver/version.d.ts +1 -1
  8. package/dist/src/driver/version.js +1 -1
  9. package/dist/src/extensions/node/fs/index.d.ts +4 -3
  10. package/dist/src/extensions/node/fs/index.js +12 -3
  11. package/dist/src/extensions/node/fs/index.js.map +1 -1
  12. package/dist/src/extensions/node/fs-promises/index.d.ts +5 -5
  13. package/dist/src/extensions/node/fs-promises/index.js +15 -6
  14. package/dist/src/extensions/node/fs-promises/index.js.map +1 -1
  15. package/dist/tests/e2e/async-await.test.js +1 -1
  16. package/dist/tests/e2e/async-fs.test.d.ts +9 -0
  17. package/dist/tests/e2e/async-fs.test.js +119 -0
  18. package/dist/tests/e2e/async-fs.test.js.map +1 -0
  19. package/dist/tests/e2e/fetch.test.js +24 -0
  20. package/dist/tests/e2e/fetch.test.js.map +1 -1
  21. package/dist/tests/e2e/gui-inline-svg.test.d.ts +11 -0
  22. package/dist/tests/e2e/gui-inline-svg.test.js +160 -0
  23. package/dist/tests/e2e/gui-inline-svg.test.js.map +1 -0
  24. package/doc/ai/contributing.md +2 -1
  25. package/doc/ai/extensions.md +4 -3
  26. package/doc/ai/language-support.md +2 -1
  27. package/doc/ai/zh-CN/contributing.md +1 -1
  28. package/doc/ai/zh-CN/extensions.md +4 -3
  29. package/doc/ai/zh-CN/language-support.md +2 -1
  30. package/doc/gui/api.md +8 -0
  31. package/doc/gui/known-issues.md +10 -9
  32. package/doc/gui/media.md +32 -3
  33. package/doc/gui.md +24 -2
  34. package/doc/implemented/async.md +16 -3
  35. package/doc/implemented/fetch.md +38 -6
  36. package/doc/implemented/node/fs-promises.md +19 -8
  37. package/doc/implemented/node.md +1 -1
  38. package/doc/implemented.md +1 -1
  39. package/doc/unimplemented/deviations.md +1 -1
  40. package/doc/unimplemented/node/async.md +6 -3
  41. package/doc/unimplemented/node/fs-promises.md +10 -6
  42. package/doc/unimplemented/node/fs.md +7 -8
  43. package/doc/unimplemented/node.md +2 -2
  44. package/doc/unimplemented/toolchain.md +1 -1
  45. package/doc/zh-CN/gui/api.md +7 -0
  46. package/doc/zh-CN/gui/known-issues.md +8 -7
  47. package/doc/zh-CN/gui/media.md +26 -2
  48. package/doc/zh-CN/gui.md +18 -1
  49. package/doc/zh-CN/implemented/async.md +13 -2
  50. package/doc/zh-CN/implemented/fetch.md +23 -4
  51. package/doc/zh-CN/implemented/node/fs-promises.md +6 -3
  52. package/doc/zh-CN/implemented/node.md +1 -1
  53. package/doc/zh-CN/implemented.md +1 -1
  54. package/doc/zh-CN/unimplemented/deviations.md +1 -1
  55. package/doc/zh-CN/unimplemented/node/async.md +6 -3
  56. package/doc/zh-CN/unimplemented/node/fs-promises.md +4 -4
  57. package/doc/zh-CN/unimplemented/node/fs.md +2 -2
  58. package/doc/zh-CN/unimplemented/node.md +2 -2
  59. package/doc/zh-CN/unimplemented/toolchain.md +1 -1
  60. package/package.json +1 -1
  61. package/runtime/ext_gui/inline_svg.cpp +219 -0
  62. package/runtime/ext_gui/inline_svg.h +41 -0
  63. package/runtime/ext_gui/layout.cpp +18 -0
  64. package/runtime/ext_gui/layout.h +7 -0
  65. package/runtime/ext_gui/layout_flow.cpp +4 -3
  66. package/runtime/ext_gui/paint.cpp +14 -0
  67. package/runtime/ext_gui/paint.h +5 -1
  68. package/runtime/ext_gui/renderer_image.cpp +53 -25
  69. package/runtime/ext_gui/style_ua.cpp +3 -0
  70. package/runtime/ext_gui/svg.cpp +122 -39
  71. package/runtime/ext_gui/svg.h +14 -3
  72. package/runtime/ext_gui/window.cpp +28 -0
  73. package/runtime/ext_node/fs/async_api.c +314 -0
  74. package/runtime/ext_node/fs/async_complete.c +99 -0
  75. package/runtime/ext_node/fs/async_fh.c +206 -0
  76. package/runtime/ext_node/fs/async_job.c +81 -0
  77. package/runtime/ext_node/fs/async_job.h +132 -0
  78. package/runtime/ext_node/fs/async_worker.c +534 -0
  79. package/runtime/ext_node/fs/async_worker_dir.c +439 -0
  80. package/runtime/ext_node/fs/async_worker_dir.h +33 -0
  81. package/runtime/ext_node/fs/async_worker_fd.c +74 -0
  82. package/runtime/ext_node/fs/dir.c +60 -15
  83. package/runtime/ext_node/fs/fs_common.h +9 -0
  84. package/runtime/ext_node/fs/fs_ops.c +18 -29
  85. package/runtime/ext_node/fs/parts/builtins.inc +34 -0
  86. package/runtime/ext_node/fs/parts/platform.inc +66 -0
  87. package/runtime/ext_node/fs/promises.c +44 -234
  88. package/runtime/rt_internal.h +2 -0
  89. package/runtime/xt_async_io.c +333 -0
  90. package/runtime/xt_async_io.h +47 -0
  91. package/runtime/xt_fetch/async.inc +295 -0
  92. package/runtime/xt_fetch/request.inc +6 -183
  93. package/runtime/xt_fetch/socket.inc +5 -4
  94. package/runtime/xt_fetch.c +6 -4
  95. package/runtime/xt_loop.c +44 -3
  96. package/src/driver/compiler.ts +1 -1
  97. package/src/driver/runtime-objects.ts +1 -0
  98. package/src/driver/version.ts +1 -1
  99. package/src/extensions/node/fs/index.ts +12 -3
  100. package/src/extensions/node/fs-promises/index.ts +15 -6
@@ -0,0 +1,534 @@
1
+ /*
2
+ * Worker half of `fs/promises`: the blocking syscalls.
3
+ *
4
+ * Everything in this file runs on an async I/O worker thread. It may only touch
5
+ * plain C memory — no `xt_value`, no collector, no exceptions. Results are
6
+ * stored on the job and interpreted by async_complete.c on the main thread.
7
+ */
8
+
9
+ #include "async_job.h"
10
+ #include "async_worker_dir.h"
11
+
12
+ #include <errno.h>
13
+ #include <stdio.h>
14
+ #include <time.h>
15
+
16
+ #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
17
+ #include <sys/mount.h>
18
+ #elif defined(__linux__)
19
+ #include <sys/vfs.h>
20
+ #endif
21
+
22
+ #if defined(_WIN32)
23
+ #include <io.h>
24
+ #include <windows.h>
25
+ #define xt_fs_fstat_fn xt_fs_win_fstat
26
+ #else
27
+ #include <fcntl.h>
28
+ #include <sys/time.h>
29
+ #include <unistd.h>
30
+ #define xt_fs_fstat_fn fstat
31
+ #endif
32
+
33
+ /* -- small file helpers ---------------------------------------------------- */
34
+
35
+ int xt_fs_worker_read_fd(int fd, unsigned char **out, size_t *out_length) {
36
+ size_t capacity = 8192;
37
+ size_t length = 0;
38
+ unsigned char *bytes = (unsigned char *)malloc(capacity);
39
+ if (!bytes) return -1;
40
+ for (;;) {
41
+ if (length == capacity) {
42
+ capacity *= 2;
43
+ unsigned char *grown = (unsigned char *)realloc(bytes, capacity);
44
+ if (!grown) {
45
+ free(bytes);
46
+ return -1;
47
+ }
48
+ bytes = grown;
49
+ }
50
+ ssize_t count = XT_FS_READ(fd, bytes + length, capacity - length);
51
+ if (count < 0) {
52
+ int saved = errno;
53
+ free(bytes);
54
+ errno = saved;
55
+ return -1;
56
+ }
57
+ if (count == 0) break;
58
+ length += (size_t)count;
59
+ }
60
+ *out = bytes;
61
+ *out_length = length;
62
+ return 0;
63
+ }
64
+
65
+ int xt_fs_worker_read_file(const char *path, unsigned char **out, size_t *out_length) {
66
+ int fd = XT_FS_OPEN(path, XT_FS_RDONLY | XT_FS_BINARY_FLAG, 0);
67
+ if (fd < 0) return -1;
68
+ int status = xt_fs_worker_read_fd(fd, out, out_length);
69
+ int saved = errno;
70
+ XT_FS_CLOSE(fd);
71
+ errno = saved;
72
+ return status;
73
+ }
74
+
75
+ int xt_fs_worker_write_fd(int fd, const unsigned char *data, size_t length) {
76
+ size_t written = 0;
77
+ while (written < length) {
78
+ ssize_t count = XT_FS_WRITE(fd, data + written, length - written);
79
+ if (count <= 0) return -1;
80
+ written += (size_t)count;
81
+ }
82
+ return 0;
83
+ }
84
+
85
+ int xt_fs_worker_write_file(const char *path, const unsigned char *data, size_t length, int append) {
86
+ int flags = XT_FS_WRONLY | XT_FS_CREAT | XT_FS_BINARY_FLAG | (append ? XT_FS_APPEND : XT_FS_TRUNC);
87
+ int fd = XT_FS_OPEN(path, flags, 0666);
88
+ if (fd < 0) return -1;
89
+ int status = xt_fs_worker_write_fd(fd, data, length);
90
+ int saved = errno;
91
+ if (XT_FS_CLOSE(fd) != 0 && status == 0) {
92
+ status = -1;
93
+ saved = errno;
94
+ }
95
+ errno = saved;
96
+ return status;
97
+ }
98
+
99
+ int xt_fs_entry_push(xt_fs_job *job, const char *name, const char *bare, unsigned int mode) {
100
+ xt_fs_entry *grown = (xt_fs_entry *)realloc(job->entries, sizeof(xt_fs_entry) * (job->entry_count + 1));
101
+ if (!grown) return -1;
102
+ job->entries = grown;
103
+ xt_fs_entry *entry = &job->entries[job->entry_count];
104
+ entry->name = xt_fs_copy_cstr(name);
105
+ entry->bare = bare && strcmp(bare, name) != 0 ? xt_fs_copy_cstr(bare) : entry->name;
106
+ entry->mode = mode;
107
+ if (!entry->name || !entry->bare) return -1;
108
+ job->entry_count++;
109
+ return 0;
110
+ }
111
+
112
+ char *xt_fs_worker_join(const char *dir, const char *name) {
113
+ size_t dirLength = strlen(dir);
114
+ int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\';
115
+ size_t size = dirLength + (needsSlash ? 1 : 0) + strlen(name) + 1;
116
+ char *path = (char *)malloc(size);
117
+ if (!path) return NULL;
118
+ snprintf(path, size, "%s%s%s", dir, needsSlash ? "/" : "", name);
119
+ return path;
120
+ }
121
+
122
+ /* -- path operations ------------------------------------------------------- */
123
+
124
+ static void xt_fs_worker_mkdir(xt_fs_job *job) {
125
+ if (!job->path) {
126
+ xt_fs_job_fail(job, EINVAL, "mkdir", NULL);
127
+ return;
128
+ }
129
+ if (!job->recursive) {
130
+ if (xt_fs_mkdir_mode(job->path, job->mode) != 0 && errno != EEXIST) {
131
+ xt_fs_job_fail(job, errno, "mkdir", job->path);
132
+ }
133
+ return;
134
+ }
135
+ if (xt_fs_mkdir_parents(job->path, job->mode) != 0) {
136
+ xt_fs_job_fail(job, errno, "mkdir", job->path);
137
+ }
138
+ }
139
+
140
+ static void xt_fs_worker_realpath(xt_fs_job *job) {
141
+ if (!job->path) {
142
+ xt_fs_job_fail(job, EINVAL, "resolve", NULL);
143
+ return;
144
+ }
145
+ #if defined(_WIN32)
146
+ char *resolved = _fullpath(NULL, job->path, 0);
147
+ #else
148
+ char *resolved = realpath(job->path, NULL);
149
+ #endif
150
+ if (!resolved) {
151
+ xt_fs_job_fail(job, errno, "resolve", job->path);
152
+ return;
153
+ }
154
+ job->string = resolved;
155
+ }
156
+
157
+ static void xt_fs_worker_statfs(xt_fs_job *job) {
158
+ #if defined(_WIN32) || defined(_AIX) || defined(__sun)
159
+ (void)job;
160
+ for (int i = 0; i < 7; i++) job->numbers[i] = 0;
161
+ #else
162
+ if (!job->path) {
163
+ xt_fs_job_fail(job, EINVAL, "statfs", NULL);
164
+ return;
165
+ }
166
+ struct statfs info;
167
+ if (statfs(job->path, &info) != 0) {
168
+ xt_fs_job_fail(job, errno, "statfs", job->path);
169
+ return;
170
+ }
171
+ job->numbers[0] = (long long)info.f_type;
172
+ job->numbers[1] = (long long)info.f_bsize;
173
+ job->numbers[2] = (long long)info.f_blocks;
174
+ job->numbers[3] = (long long)info.f_bfree;
175
+ job->numbers[4] = (long long)info.f_bavail;
176
+ job->numbers[5] = (long long)info.f_files;
177
+ job->numbers[6] = (long long)info.f_ffree;
178
+ #endif
179
+ }
180
+
181
+ static void xt_fs_worker_mkdtemp(xt_fs_job *job) {
182
+ if (!job->path) {
183
+ xt_fs_job_fail(job, EINVAL, "mkdtemp", NULL);
184
+ return;
185
+ }
186
+ size_t length = strlen(job->path);
187
+ char *dir = (char *)malloc(length + 7);
188
+ if (!dir) {
189
+ xt_fs_job_fail(job, ENOMEM, "mkdtemp", job->path);
190
+ return;
191
+ }
192
+ memcpy(dir, job->path, length);
193
+ static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789";
194
+ /* Concurrent mkdtemp jobs must not walk the same candidate sequence; mix in
195
+ * this job's allocation address, which differs per thread. */
196
+ unsigned int noise = (unsigned int)(uintptr_t)dir;
197
+ for (int attempt = 0; attempt < 100; attempt++) {
198
+ unsigned int seed = (unsigned int)time(NULL) ^ noise ^ (unsigned int)(attempt * 2654435761u);
199
+ for (int i = 0; i < 6; i++) {
200
+ dir[length + (size_t)i] = alphabet[seed % 36u];
201
+ seed = seed * 1103515245u + 12345u;
202
+ }
203
+ dir[length + 6] = '\0';
204
+ if (xt_fs_mkdir_one(dir) == 0) {
205
+ job->string = dir;
206
+ return;
207
+ }
208
+ if (errno != EEXIST) break;
209
+ }
210
+ int saved = errno;
211
+ free(dir);
212
+ xt_fs_job_fail(job, saved, "mkdtemp", job->path);
213
+ }
214
+
215
+ #if defined(_WIN32)
216
+ static void xt_fs_worker_filetime(double seconds, FILETIME *out) {
217
+ long long ticks = (long long)(seconds * 10000000.0) + 116444736000000000LL;
218
+ unsigned long long value = (unsigned long long)ticks;
219
+ out->dwLowDateTime = (DWORD)value;
220
+ out->dwHighDateTime = (DWORD)(value >> 32);
221
+ }
222
+
223
+ static int xt_fs_worker_set_handle_times(HANDLE handle, double atime, double mtime) {
224
+ FILETIME accessTime;
225
+ FILETIME modifyTime;
226
+ FILETIME *accessPtr = NULL;
227
+ FILETIME *modifyPtr = NULL;
228
+ if (atime == atime) {
229
+ xt_fs_worker_filetime(atime, &accessTime);
230
+ accessPtr = &accessTime;
231
+ }
232
+ if (mtime == mtime) {
233
+ xt_fs_worker_filetime(mtime, &modifyTime);
234
+ modifyPtr = &modifyTime;
235
+ }
236
+ return SetFileTime(handle, NULL, accessPtr, modifyPtr) ? 0 : -1;
237
+ }
238
+
239
+ static int xt_fs_worker_set_path_times(const char *path, double atime, double mtime) {
240
+ HANDLE handle = CreateFileA(path, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
241
+ NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
242
+ if (handle == INVALID_HANDLE_VALUE) return -1;
243
+ int status = xt_fs_worker_set_handle_times(handle, atime, mtime);
244
+ int saved = errno;
245
+ CloseHandle(handle);
246
+ errno = saved;
247
+ return status;
248
+ }
249
+ #endif
250
+
251
+ static void xt_fs_worker_utimes(xt_fs_job *job, int follow) {
252
+ if (!job->path) {
253
+ xt_fs_job_fail(job, EINVAL, "utime", NULL);
254
+ return;
255
+ }
256
+ #if defined(_WIN32)
257
+ (void)follow;
258
+ if (xt_fs_worker_set_path_times(job->path, job->atime, job->mtime) != 0) {
259
+ xt_fs_job_fail(job, errno ? errno : EPERM, "utime", job->path);
260
+ }
261
+ #else
262
+ struct timeval times[2];
263
+ times[0].tv_sec = (time_t)job->atime;
264
+ times[0].tv_usec = (suseconds_t)((job->atime - (double)times[0].tv_sec) * 1000000.0);
265
+ times[1].tv_sec = (time_t)job->mtime;
266
+ times[1].tv_usec = (suseconds_t)((job->mtime - (double)times[1].tv_sec) * 1000000.0);
267
+ #if defined(__linux__)
268
+ int status = follow ? utimes(job->path, times) : lutimes(job->path, times);
269
+ #else
270
+ (void)follow;
271
+ int status = utimes(job->path, times);
272
+ #endif
273
+ if (status != 0) xt_fs_job_fail(job, errno, "utime", job->path);
274
+ #endif
275
+ }
276
+
277
+ /* -- FileHandle operations ------------------------------------------------- */
278
+
279
+ static void xt_fs_worker_fd_utimes(xt_fs_job *job) {
280
+ #if defined(_WIN32)
281
+ intptr_t osHandle = _get_osfhandle(job->fd);
282
+ if (osHandle == -1 || xt_fs_worker_set_handle_times((HANDLE)osHandle, job->atime, job->mtime) != 0) {
283
+ xt_fs_job_fail(job, errno ? errno : EPERM, "futime", NULL);
284
+ }
285
+ #else
286
+ struct timeval times[2];
287
+ times[0].tv_sec = (time_t)job->atime;
288
+ times[0].tv_usec = (suseconds_t)((job->atime - (double)times[0].tv_sec) * 1000000.0);
289
+ times[1].tv_sec = (time_t)job->mtime;
290
+ times[1].tv_usec = (suseconds_t)((job->mtime - (double)times[1].tv_sec) * 1000000.0);
291
+ if (futimes(job->fd, times) != 0) xt_fs_job_fail(job, errno, "futime", NULL);
292
+ #endif
293
+ }
294
+
295
+ /* -- dispatch -------------------------------------------------------------- */
296
+
297
+ void xt_fs_job_run(xt_async_job *base) {
298
+ xt_fs_job *job = (xt_fs_job *)base;
299
+ switch (job->kind) {
300
+ case XT_FS_JOB_READ_FILE:
301
+ if (!job->path) {
302
+ xt_fs_job_fail(job, EINVAL, "open", NULL);
303
+ break;
304
+ }
305
+ if (xt_fs_worker_read_file(job->path, &job->bytes, &job->bytes_length) != 0) {
306
+ xt_fs_job_fail(job, errno, "open", job->path);
307
+ }
308
+ break;
309
+ case XT_FS_JOB_WRITE_FILE:
310
+ if (!job->path) {
311
+ xt_fs_job_fail(job, EINVAL, "open", NULL);
312
+ break;
313
+ }
314
+ if (xt_fs_worker_write_file(job->path, job->data, job->data_length, job->flags != 0) != 0) {
315
+ xt_fs_job_fail(job, errno, "open", job->path);
316
+ }
317
+ break;
318
+ case XT_FS_JOB_MKDIR: xt_fs_worker_mkdir(job); break;
319
+ case XT_FS_JOB_READDIR: xt_fs_worker_readdir(job); break;
320
+ case XT_FS_JOB_RM: xt_fs_worker_remove_tree(job); break;
321
+ case XT_FS_JOB_UNLINK:
322
+ if (!job->path) {
323
+ xt_fs_job_fail(job, EINVAL, "unlink", NULL);
324
+ break;
325
+ }
326
+ #if defined(_WIN32)
327
+ if (_unlink(job->path) != 0) xt_fs_job_fail(job, errno, "unlink", job->path);
328
+ #else
329
+ if (unlink(job->path) != 0) xt_fs_job_fail(job, errno, "unlink", job->path);
330
+ #endif
331
+ break;
332
+ case XT_FS_JOB_RMDIR:
333
+ if (!job->path || xt_fs_rmdir_one(job->path) != 0) xt_fs_job_fail(job, errno, "rmdir", job->path);
334
+ break;
335
+ case XT_FS_JOB_RENAME:
336
+ if (!job->path || !job->path2 || rename(job->path, job->path2) != 0) {
337
+ xt_fs_job_fail(job, errno, "rename", job->path);
338
+ }
339
+ break;
340
+ case XT_FS_JOB_COPY_FILE: xt_fs_worker_copy_file(job); break;
341
+ case XT_FS_JOB_CP: xt_fs_worker_copy_tree(job); break;
342
+ case XT_FS_JOB_REALPATH: xt_fs_worker_realpath(job); break;
343
+ case XT_FS_JOB_STAT:
344
+ case XT_FS_JOB_LSTAT:
345
+ if (!job->path) {
346
+ xt_fs_job_fail(job, EINVAL, "stat", NULL);
347
+ break;
348
+ }
349
+ if ((job->kind == XT_FS_JOB_STAT ? xt_fs_stat_fn(job->path, &job->stat)
350
+ : xt_fs_lstat_fn(job->path, &job->stat)) != 0) {
351
+ xt_fs_job_fail(job, errno, "stat", job->path);
352
+ }
353
+ break;
354
+ case XT_FS_JOB_STATFS: xt_fs_worker_statfs(job); break;
355
+ case XT_FS_JOB_ACCESS:
356
+ if (!job->path || xt_fs_access(job->path, job->mode) != 0) xt_fs_job_fail(job, errno, "access", job->path);
357
+ break;
358
+ case XT_FS_JOB_OPEN:
359
+ if (!job->path) {
360
+ xt_fs_job_fail(job, EINVAL, "open", NULL);
361
+ break;
362
+ }
363
+ #if defined(_WIN32)
364
+ job->result_fd = _open(job->path, job->flags, job->mode);
365
+ #else
366
+ job->result_fd = open(job->path, job->flags, (mode_t)job->mode);
367
+ #endif
368
+ if (job->result_fd < 0) xt_fs_job_fail(job, errno, "open", job->path);
369
+ break;
370
+ case XT_FS_JOB_CHMOD:
371
+ case XT_FS_JOB_LCHMOD:
372
+ #if defined(_WIN32)
373
+ if (job->kind == XT_FS_JOB_LCHMOD) break;
374
+ if (!job->path || _chmod(job->path, job->mode) != 0) xt_fs_job_fail(job, errno, "chmod", job->path);
375
+ #else
376
+ if (!job->path || chmod(job->path, (mode_t)job->mode) != 0) xt_fs_job_fail(job, errno, "chmod", job->path);
377
+ #endif
378
+ break;
379
+ case XT_FS_JOB_CHOWN:
380
+ case XT_FS_JOB_LCHOWN:
381
+ #if defined(_WIN32)
382
+ break;
383
+ #else
384
+ if (!job->path ||
385
+ (job->kind == XT_FS_JOB_CHOWN ? chown(job->path, (uid_t)job->uid, (gid_t)job->gid)
386
+ : lchown(job->path, (uid_t)job->uid, (gid_t)job->gid)) != 0) {
387
+ xt_fs_job_fail(job, errno, "chown", job->path);
388
+ }
389
+ break;
390
+ #endif
391
+ case XT_FS_JOB_TRUNCATE:
392
+ if (!job->path) {
393
+ xt_fs_job_fail(job, EINVAL, "truncate", NULL);
394
+ break;
395
+ }
396
+ #if defined(_WIN32)
397
+ {
398
+ int fd = _open(job->path, _O_RDWR | _O_BINARY);
399
+ int status = fd < 0 ? -1 : _chsize_s(fd, job->length);
400
+ int saved = errno;
401
+ if (fd >= 0) _close(fd);
402
+ if (status != 0) xt_fs_job_fail(job, saved, "truncate", job->path);
403
+ }
404
+ #else
405
+ if (truncate(job->path, (off_t)job->length) != 0) xt_fs_job_fail(job, errno, "truncate", job->path);
406
+ #endif
407
+ break;
408
+ case XT_FS_JOB_UTIMES: xt_fs_worker_utimes(job, 1); break;
409
+ case XT_FS_JOB_LUTIMES: xt_fs_worker_utimes(job, 0); break;
410
+ case XT_FS_JOB_LINK:
411
+ #if defined(_WIN32)
412
+ if (!job->path || !job->path2 || !CreateHardLinkA(job->path2, job->path, NULL)) {
413
+ xt_fs_job_fail(job, errno ? errno : EPERM, "link", job->path2);
414
+ }
415
+ #else
416
+ if (!job->path || !job->path2 || link(job->path, job->path2) != 0) {
417
+ xt_fs_job_fail(job, errno, "link", job->path2);
418
+ }
419
+ #endif
420
+ break;
421
+ case XT_FS_JOB_SYMLINK:
422
+ #if defined(_WIN32)
423
+ {
424
+ DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
425
+ if (job->text && (strcmp(job->text, "dir") == 0 || strcmp(job->text, "junction") == 0)) {
426
+ flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
427
+ }
428
+ if (!job->path || !job->path2 || !CreateSymbolicLinkA(job->path2, job->path, flags)) {
429
+ xt_fs_job_fail(job, errno ? errno : EPERM, "symlink", job->path2);
430
+ }
431
+ }
432
+ #else
433
+ if (!job->path || !job->path2 || symlink(job->path, job->path2) != 0) {
434
+ xt_fs_job_fail(job, errno, "symlink", job->path2);
435
+ }
436
+ #endif
437
+ break;
438
+ case XT_FS_JOB_READLINK:
439
+ #if defined(_WIN32)
440
+ xt_fs_job_fail(job, ENOSYS, "readlink", job->path);
441
+ #else
442
+ if (!job->path) {
443
+ xt_fs_job_fail(job, ENOENT, "readlink", NULL);
444
+ break;
445
+ }
446
+ {
447
+ size_t capacity = 256;
448
+ for (;;) {
449
+ char *buffer = (char *)malloc(capacity);
450
+ if (!buffer) {
451
+ xt_fs_job_fail(job, ENOMEM, "readlink", job->path);
452
+ break;
453
+ }
454
+ ssize_t length = readlink(job->path, buffer, capacity);
455
+ if (length < 0) {
456
+ int saved = errno;
457
+ free(buffer);
458
+ xt_fs_job_fail(job, saved, "readlink", job->path);
459
+ break;
460
+ }
461
+ if ((size_t)length < capacity) {
462
+ buffer[length] = '\0';
463
+ job->string = buffer;
464
+ break;
465
+ }
466
+ free(buffer);
467
+ capacity *= 2;
468
+ }
469
+ }
470
+ #endif
471
+ break;
472
+ case XT_FS_JOB_MKDTEMP: xt_fs_worker_mkdtemp(job); break;
473
+ case XT_FS_JOB_OPENDIR: xt_fs_worker_opendir(job); break;
474
+ case XT_FS_JOB_GLOB: xt_fs_worker_glob(job); break;
475
+ case XT_FS_JOB_FD_READ: xt_fs_worker_fd_read(job); break;
476
+ case XT_FS_JOB_FD_WRITE: xt_fs_worker_fd_write(job); break;
477
+ case XT_FS_JOB_FD_READ_FILE:
478
+ if (xt_fs_worker_read_fd(job->fd, &job->bytes, &job->bytes_length) != 0) {
479
+ xt_fs_job_fail(job, errno, "read", NULL);
480
+ }
481
+ break;
482
+ case XT_FS_JOB_FD_WRITE_FILE:
483
+ if (xt_fs_worker_write_fd(job->fd, job->data, job->data_length) != 0) {
484
+ xt_fs_job_fail(job, errno, "write", NULL);
485
+ }
486
+ break;
487
+ case XT_FS_JOB_FD_CLOSE:
488
+ if (XT_FS_CLOSE(job->fd) != 0) xt_fs_job_fail(job, errno, "close", NULL);
489
+ break;
490
+ case XT_FS_JOB_FD_STAT:
491
+ if (xt_fs_fstat_fn(job->fd, &job->stat) != 0) xt_fs_job_fail(job, errno, "fstat", NULL);
492
+ break;
493
+ case XT_FS_JOB_FD_TRUNCATE:
494
+ #if defined(_WIN32)
495
+ if (_chsize_s(job->fd, job->length) != 0) xt_fs_job_fail(job, errno, "ftruncate", NULL);
496
+ #else
497
+ if (ftruncate(job->fd, (off_t)job->length) != 0) xt_fs_job_fail(job, errno, "ftruncate", NULL);
498
+ #endif
499
+ break;
500
+ case XT_FS_JOB_FD_CHMOD:
501
+ #if defined(_WIN32)
502
+ break;
503
+ #else
504
+ if (fchmod(job->fd, (mode_t)job->mode) != 0) xt_fs_job_fail(job, errno, "fchmod", NULL);
505
+ #endif
506
+ break;
507
+ case XT_FS_JOB_FD_CHOWN:
508
+ #if defined(_WIN32)
509
+ break;
510
+ #else
511
+ if (fchown(job->fd, (uid_t)job->uid, (gid_t)job->gid) != 0) xt_fs_job_fail(job, errno, "fchown", NULL);
512
+ #endif
513
+ break;
514
+ case XT_FS_JOB_FD_UTIMES: xt_fs_worker_fd_utimes(job); break;
515
+ case XT_FS_JOB_FD_SYNC:
516
+ #if defined(_WIN32)
517
+ if (_commit(job->fd) != 0) xt_fs_job_fail(job, errno, "fsync", NULL);
518
+ #else
519
+ if (fsync(job->fd) != 0) xt_fs_job_fail(job, errno, "fsync", NULL);
520
+ #endif
521
+ break;
522
+ case XT_FS_JOB_FD_DATASYNC:
523
+ #if defined(_WIN32)
524
+ if (_commit(job->fd) != 0) xt_fs_job_fail(job, errno, "fdatasync", NULL);
525
+ #else
526
+ #if defined(__APPLE__)
527
+ if (fsync(job->fd) != 0) xt_fs_job_fail(job, errno, "fdatasync", NULL);
528
+ #else
529
+ if (fdatasync(job->fd) != 0) xt_fs_job_fail(job, errno, "fdatasync", NULL);
530
+ #endif
531
+ #endif
532
+ break;
533
+ }
534
+ }