xbintsc 0.3.13 → 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.
- package/dist/src/codegen/generator/calls/access.js +13 -0
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +8 -2
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +6 -0
- package/dist/src/codegen/generator/context.js +24 -2
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.js +5 -0
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/driver/compiler.js +15 -3
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +1 -1
- package/dist/src/driver/modules.js +235 -25
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/extensions/node/fs/fs-ops.d.ts +5 -2
- package/dist/src/extensions/node/fs/fs-ops.js +45 -6
- package/dist/src/extensions/node/fs/fs-ops.js.map +1 -1
- package/dist/src/extensions/node/fs/index.d.ts +6 -2
- package/dist/src/extensions/node/fs/index.js +22 -3
- package/dist/src/extensions/node/fs/index.js.map +1 -1
- package/dist/src/extensions/node/fs-promises/index.d.ts +4 -3
- package/dist/src/extensions/node/fs-promises/index.js +31 -3
- package/dist/src/extensions/node/fs-promises/index.js.map +1 -1
- package/dist/tests/cli/main.test.js +8 -0
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/driver/compiler.test.js +28 -1
- package/dist/tests/driver/compiler.test.js.map +1 -1
- package/dist/tests/driver/modules.test.js +91 -4
- package/dist/tests/driver/modules.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +240 -0
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +12 -0
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/harness.js +11 -8
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/e2e/node-modules.test.js +1 -1
- package/dist/tests/e2e/node-modules.test.js.map +1 -1
- package/dist/tests/e2e/npm-modules.test.d.ts +9 -0
- package/dist/tests/e2e/npm-modules.test.js +55 -0
- package/dist/tests/e2e/npm-modules.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage.test.d.ts +11 -0
- package/dist/tests/e2e/runtime-coverage.test.js +646 -0
- package/dist/tests/e2e/runtime-coverage.test.js.map +1 -0
- package/package.json +1 -1
- package/runtime/ext_node/buffer/parts/prototype.inc +7 -0
- package/runtime/ext_node/buffer/parts/search.inc +8 -4
- package/runtime/ext_node/child_process/child_process.c +5 -0
- package/runtime/ext_node/fs/constants.c +113 -0
- package/runtime/ext_node/fs/copy_ops.c +145 -0
- package/runtime/ext_node/fs/dir.c +101 -0
- package/runtime/ext_node/fs/fd_ops.c +405 -0
- package/runtime/ext_node/fs/fs_common.h +388 -5
- package/runtime/ext_node/fs/fs_ops.c +100 -139
- package/runtime/ext_node/fs/glob.c +232 -0
- package/runtime/ext_node/fs/link_ops.c +91 -0
- package/runtime/ext_node/fs/meta_ops.c +403 -0
- package/runtime/ext_node/fs/promises.c +275 -60
- package/runtime/ext_node/fs/read_file.c +8 -4
- package/runtime/ext_node/fs/watch.c +52 -0
- package/runtime/ext_node/fs/write_file.c +20 -11
- package/runtime/ext_node/stream/stream.c +19 -2
- package/runtime/rt.h +9 -0
- package/runtime/xt_io.c +21 -0
- package/src/codegen/generator/calls/access.ts +15 -0
- package/src/codegen/generator/calls/invocation.ts +8 -2
- package/src/codegen/generator/context.ts +30 -2
- package/src/codegen/generator/expressions/primary.ts +5 -0
- package/src/driver/compiler.ts +16 -3
- package/src/driver/modules.ts +251 -26
- package/src/extensions/node/fs/fs-ops.ts +48 -6
- package/src/extensions/node/fs/index.ts +25 -3
- package/src/extensions/node/fs-promises/index.ts +33 -4
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
* existsSync, readdirSync, mkdirSync, rmSync, unlinkSync, rmdirSync,
|
|
6
6
|
* renameSync, copyFileSync, realpathSync, statSync, lstatSync
|
|
7
7
|
*
|
|
8
|
-
* Error handling
|
|
9
|
-
*
|
|
10
|
-
* failures print to stderr and return `undefined` (or `false` for
|
|
11
|
-
* `existsSync`) instead of throwing `ENOENT`-style exceptions.
|
|
8
|
+
* Error handling follows Node: failures throw a shaped Error (see
|
|
9
|
+
* `xt_fs_error`), which `fs/promises` converts into a rejected Promise.
|
|
12
10
|
*/
|
|
13
11
|
|
|
14
12
|
#include "rt.h"
|
|
@@ -16,106 +14,6 @@
|
|
|
16
14
|
|
|
17
15
|
#include <errno.h>
|
|
18
16
|
|
|
19
|
-
#if defined(_WIN32)
|
|
20
|
-
#include <direct.h>
|
|
21
|
-
#include <io.h>
|
|
22
|
-
#include <sys/stat.h>
|
|
23
|
-
#include <sys/types.h>
|
|
24
|
-
|
|
25
|
-
typedef struct _stat xt_fs_stat_t;
|
|
26
|
-
#define xt_fs_stat_fn _stat
|
|
27
|
-
#define xt_fs_lstat_fn _stat
|
|
28
|
-
#define xt_fs_access _access
|
|
29
|
-
|
|
30
|
-
#ifndef F_OK
|
|
31
|
-
#define F_OK 0
|
|
32
|
-
#endif
|
|
33
|
-
#ifndef S_ISREG
|
|
34
|
-
#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
|
|
35
|
-
#endif
|
|
36
|
-
#ifndef S_ISDIR
|
|
37
|
-
#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
|
|
38
|
-
#endif
|
|
39
|
-
#ifndef S_ISLNK
|
|
40
|
-
#define S_ISLNK(m) 0
|
|
41
|
-
#endif
|
|
42
|
-
#ifndef S_ISFIFO
|
|
43
|
-
#define S_ISFIFO(m) 0
|
|
44
|
-
#endif
|
|
45
|
-
#ifndef S_ISSOCK
|
|
46
|
-
#define S_ISSOCK(m) 0
|
|
47
|
-
#endif
|
|
48
|
-
#ifndef S_ISBLK
|
|
49
|
-
#define S_ISBLK(m) 0
|
|
50
|
-
#endif
|
|
51
|
-
#ifndef S_ISCHR
|
|
52
|
-
#define S_ISCHR(m) (((m) & _S_IFMT) == _S_IFCHR)
|
|
53
|
-
#endif
|
|
54
|
-
|
|
55
|
-
/* Directory iteration backed by the CRT's _findfirst/_findnext. */
|
|
56
|
-
typedef struct {
|
|
57
|
-
intptr_t handle;
|
|
58
|
-
struct _finddata_t entry;
|
|
59
|
-
} xt_fs_dir;
|
|
60
|
-
|
|
61
|
-
static int xt_fs_dir_open(xt_fs_dir *dir, const char *path) {
|
|
62
|
-
size_t length = strlen(path);
|
|
63
|
-
char *pattern = (char *)malloc(length + 3);
|
|
64
|
-
if (!pattern) return 0;
|
|
65
|
-
memcpy(pattern, path, length);
|
|
66
|
-
pattern[length] = '/';
|
|
67
|
-
pattern[length + 1] = '*';
|
|
68
|
-
pattern[length + 2] = '\0';
|
|
69
|
-
dir->handle = _findfirst(pattern, &dir->entry);
|
|
70
|
-
free(pattern);
|
|
71
|
-
return dir->handle != -1;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
static int xt_fs_dir_next(xt_fs_dir *dir) { return _findnext(dir->handle, &dir->entry) == 0; }
|
|
75
|
-
|
|
76
|
-
static const char *xt_fs_dir_name(xt_fs_dir *dir) { return dir->entry.name; }
|
|
77
|
-
|
|
78
|
-
static void xt_fs_dir_close(xt_fs_dir *dir) { _findclose(dir->handle); }
|
|
79
|
-
#else
|
|
80
|
-
#include <dirent.h>
|
|
81
|
-
#include <sys/stat.h>
|
|
82
|
-
#include <sys/types.h>
|
|
83
|
-
#include <unistd.h>
|
|
84
|
-
|
|
85
|
-
typedef struct stat xt_fs_stat_t;
|
|
86
|
-
#define xt_fs_stat_fn stat
|
|
87
|
-
#define xt_fs_lstat_fn lstat
|
|
88
|
-
#define xt_fs_access access
|
|
89
|
-
|
|
90
|
-
typedef struct {
|
|
91
|
-
DIR *handle;
|
|
92
|
-
struct dirent *entry;
|
|
93
|
-
} xt_fs_dir;
|
|
94
|
-
|
|
95
|
-
static int xt_fs_dir_open(xt_fs_dir *dir, const char *path) {
|
|
96
|
-
dir->handle = opendir(path);
|
|
97
|
-
dir->entry = NULL;
|
|
98
|
-
return dir->handle != NULL;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
static int xt_fs_dir_next(xt_fs_dir *dir) {
|
|
102
|
-
dir->entry = readdir(dir->handle);
|
|
103
|
-
return dir->entry != NULL;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
static const char *xt_fs_dir_name(xt_fs_dir *dir) { return dir->entry->d_name; }
|
|
107
|
-
|
|
108
|
-
static void xt_fs_dir_close(xt_fs_dir *dir) { closedir(dir->handle); }
|
|
109
|
-
#endif
|
|
110
|
-
|
|
111
|
-
#if defined(_WIN32)
|
|
112
|
-
#define xt_fs_mkdir_one(path) _mkdir(path)
|
|
113
|
-
#define xt_fs_rmdir_one(path) _rmdir(path)
|
|
114
|
-
#else
|
|
115
|
-
#define xt_fs_mkdir_one(path) mkdir(path, 0777)
|
|
116
|
-
#define xt_fs_rmdir_one(path) rmdir(path)
|
|
117
|
-
#endif
|
|
118
|
-
|
|
119
17
|
/* ------------------------------------------------------------------------- */
|
|
120
18
|
/* existsSync */
|
|
121
19
|
/* ------------------------------------------------------------------------- */
|
|
@@ -138,7 +36,7 @@ static void xt_fs_define_stat_method(xt_value object, const char *name, unsigned
|
|
|
138
36
|
/* Build the Node `Dirent`-shaped object (`name` plus stat predicates) for a
|
|
139
37
|
single directory entry. Types come from `lstat`, matching Node's `d_type`
|
|
140
38
|
based `Dirent` (a symlink reports `isSymbolicLink()` true). */
|
|
141
|
-
|
|
39
|
+
xt_value xt_node_dirent_result(const char *dir, const char *name) {
|
|
142
40
|
size_t size = strlen(dir) + strlen(name) + 2;
|
|
143
41
|
char *full = (char *)malloc(size);
|
|
144
42
|
unsigned int mode = 0;
|
|
@@ -161,45 +59,96 @@ static xt_value xt_node_dirent_result(const char *dir, const char *name) {
|
|
|
161
59
|
return object;
|
|
162
60
|
}
|
|
163
61
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
62
|
+
typedef struct {
|
|
63
|
+
xt_value *items;
|
|
64
|
+
size_t count;
|
|
65
|
+
size_t capacity;
|
|
66
|
+
} xt_fs_list;
|
|
67
|
+
|
|
68
|
+
static void xt_fs_list_push(xt_fs_list *list, xt_value value) {
|
|
69
|
+
if (list->count == list->capacity) {
|
|
70
|
+
list->capacity = list->capacity ? list->capacity * 2 : 16;
|
|
71
|
+
xt_value *grown = (xt_value *)realloc(list->items, sizeof(xt_value) * list->capacity);
|
|
72
|
+
if (!grown) return;
|
|
73
|
+
list->items = grown;
|
|
74
|
+
}
|
|
75
|
+
list->items[list->count++] = value;
|
|
76
|
+
}
|
|
168
77
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
78
|
+
/* Depth-first walk used by `readdirSync(path, { recursive: true })`. `prefix`
|
|
79
|
+
is the directory path relative to `base` (empty for the root, otherwise
|
|
80
|
+
ending in `/`). */
|
|
81
|
+
static void xt_fs_readdir_into(const char *base, const char *prefix, int withFileTypes, int recursive,
|
|
82
|
+
xt_fs_list *list) {
|
|
83
|
+
size_t baseLength = strlen(base);
|
|
84
|
+
size_t prefixLength = strlen(prefix);
|
|
85
|
+
size_t dirSize = baseLength + prefixLength + 2;
|
|
86
|
+
char *dirPath = (char *)malloc(dirSize);
|
|
87
|
+
if (!dirPath) return;
|
|
88
|
+
if (prefixLength == 0) snprintf(dirPath, dirSize, "%s", base);
|
|
89
|
+
else snprintf(dirPath, dirSize, "%s/%s", base, prefix);
|
|
90
|
+
size_t dirLength = strlen(dirPath);
|
|
91
|
+
while (dirLength > 1 && dirPath[dirLength - 1] == '/') dirPath[--dirLength] = '\0';
|
|
173
92
|
|
|
174
93
|
xt_fs_dir dir;
|
|
175
|
-
if (!xt_fs_dir_open(&dir,
|
|
176
|
-
xt_fs_error("open directory",
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
size_t count = 0;
|
|
181
|
-
size_t capacity = 16;
|
|
182
|
-
xt_value *items = (xt_value *)malloc(sizeof(xt_value) * capacity);
|
|
183
|
-
if (!items) {
|
|
184
|
-
xt_fs_dir_close(&dir);
|
|
185
|
-
return xt_undefined();
|
|
94
|
+
if (!xt_fs_dir_open(&dir, dirPath)) {
|
|
95
|
+
xt_fs_error("open directory", dirPath);
|
|
96
|
+
free(dirPath);
|
|
97
|
+
return;
|
|
186
98
|
}
|
|
187
|
-
|
|
188
99
|
while (xt_fs_dir_next(&dir)) {
|
|
189
100
|
const char *name = xt_fs_dir_name(&dir);
|
|
190
101
|
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
102
|
+
size_t nameLength = strlen(name);
|
|
103
|
+
size_t relSize = prefixLength + nameLength + 1;
|
|
104
|
+
char *relative = (char *)malloc(relSize);
|
|
105
|
+
if (!relative) continue;
|
|
106
|
+
snprintf(relative, relSize, "%s%s", prefix, name);
|
|
107
|
+
xt_fs_list_push(list, withFileTypes ? xt_node_dirent_result(dirPath, name) : xt_string_from_cstr(relative));
|
|
108
|
+
|
|
109
|
+
if (recursive) {
|
|
110
|
+
size_t fullSize = dirLength + nameLength + 2;
|
|
111
|
+
char *full = (char *)malloc(fullSize);
|
|
112
|
+
if (full) {
|
|
113
|
+
snprintf(full, fullSize, "%s/%s", dirPath, name);
|
|
114
|
+
xt_fs_stat_t info;
|
|
115
|
+
if (xt_fs_lstat_fn(full, &info) == 0 && S_ISDIR(info.st_mode)) {
|
|
116
|
+
size_t childSize = relSize + 1;
|
|
117
|
+
char *child = (char *)malloc(childSize);
|
|
118
|
+
if (child) {
|
|
119
|
+
snprintf(child, childSize, "%s/", relative);
|
|
120
|
+
xt_fs_readdir_into(base, child, withFileTypes, recursive, list);
|
|
121
|
+
free(child);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
free(full);
|
|
125
|
+
}
|
|
196
126
|
}
|
|
197
|
-
|
|
127
|
+
free(relative);
|
|
198
128
|
}
|
|
199
129
|
xt_fs_dir_close(&dir);
|
|
130
|
+
free(dirPath);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
xt_value xt_node_read_dir(int32_t argc, xt_value *argv) {
|
|
134
|
+
if (argc < 1) return xt_undefined();
|
|
135
|
+
const char *path = xt_string_data(xt_to_string(argv[0]));
|
|
136
|
+
if (!path) return xt_undefined();
|
|
200
137
|
|
|
201
|
-
|
|
202
|
-
|
|
138
|
+
/* `readdirSync(path, { withFileTypes: true })` yields `Dirent` objects;
|
|
139
|
+
without the option the result is a plain array of entry names. */
|
|
140
|
+
xt_value options = argc > 1 ? argv[1] : XT_UNDEFINED;
|
|
141
|
+
int withFileTypes = XT_IS_OBJECT(options) && xt_truthy(xt_object_get_cstr(options, "withFileTypes"));
|
|
142
|
+
int recursive = XT_IS_OBJECT(options) && xt_truthy(xt_object_get_cstr(options, "recursive"));
|
|
143
|
+
|
|
144
|
+
xt_fs_list list;
|
|
145
|
+
list.items = NULL;
|
|
146
|
+
list.count = 0;
|
|
147
|
+
list.capacity = 0;
|
|
148
|
+
xt_fs_readdir_into(path, "", withFileTypes, recursive, &list);
|
|
149
|
+
|
|
150
|
+
xt_value result = xt_array_new((int32_t)list.count, list.items);
|
|
151
|
+
free(list.items);
|
|
203
152
|
return result;
|
|
204
153
|
}
|
|
205
154
|
|
|
@@ -214,7 +163,7 @@ static int xt_fs_recursive(xt_value options) {
|
|
|
214
163
|
return 1;
|
|
215
164
|
}
|
|
216
165
|
|
|
217
|
-
static int xt_fs_mkdir_recursive(const char *path) {
|
|
166
|
+
static int xt_fs_mkdir_recursive(const char *path, int mode) {
|
|
218
167
|
size_t length = strlen(path);
|
|
219
168
|
char *copy = (char *)malloc(length + 1);
|
|
220
169
|
if (!copy) return -1;
|
|
@@ -225,7 +174,7 @@ static int xt_fs_mkdir_recursive(const char *path) {
|
|
|
225
174
|
if (copy[i] != '/' && copy[i] != '\0') continue;
|
|
226
175
|
char saved = copy[i];
|
|
227
176
|
copy[i] = '\0';
|
|
228
|
-
if (
|
|
177
|
+
if (xt_fs_mkdir_mode(copy, mode) != 0 && errno != EEXIST) {
|
|
229
178
|
free(copy);
|
|
230
179
|
return -1;
|
|
231
180
|
}
|
|
@@ -240,11 +189,17 @@ xt_value xt_node_mkdir(int32_t argc, xt_value *argv) {
|
|
|
240
189
|
const char *path = xt_string_data(xt_to_string(argv[0]));
|
|
241
190
|
if (!path) return xt_undefined();
|
|
242
191
|
|
|
243
|
-
|
|
244
|
-
|
|
192
|
+
xt_value options = argc > 1 ? argv[1] : XT_UNDEFINED;
|
|
193
|
+
int mode = 0777;
|
|
194
|
+
if (XT_IS_OBJECT(options) && !XT_IS_UNDEFINED(xt_object_get_cstr(options, "mode"))) {
|
|
195
|
+
mode = (int)xt_to_number(xt_object_get_cstr(options, "mode"));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (argc > 1 && xt_fs_recursive(options)) {
|
|
199
|
+
if (xt_fs_mkdir_recursive(path, mode) != 0) xt_fs_error("create directory", path);
|
|
245
200
|
return xt_undefined();
|
|
246
201
|
}
|
|
247
|
-
if (
|
|
202
|
+
if (xt_fs_mkdir_mode(path, mode) != 0 && errno != EEXIST) xt_fs_error("create directory", path);
|
|
248
203
|
return xt_undefined();
|
|
249
204
|
}
|
|
250
205
|
|
|
@@ -319,6 +274,12 @@ xt_value xt_node_copy_file(int32_t argc, xt_value *argv) {
|
|
|
319
274
|
const char *to = xt_string_data(xt_to_string(argv[1]));
|
|
320
275
|
if (!from || !to) return xt_undefined();
|
|
321
276
|
|
|
277
|
+
int flags = argc > 2 ? (int)xt_to_number(argv[2]) : 0;
|
|
278
|
+
if ((flags & 1) && xt_fs_access(to, F_OK) == 0) {
|
|
279
|
+
xt_fs_raise_errno(EEXIST, "copyfile", to);
|
|
280
|
+
return xt_undefined();
|
|
281
|
+
}
|
|
282
|
+
|
|
322
283
|
FILE *source = fopen(from, "rb");
|
|
323
284
|
if (!source) {
|
|
324
285
|
xt_fs_error("open", from);
|
|
@@ -400,7 +361,7 @@ static double xt_fs_seconds_ms(long seconds, long nanoseconds) {
|
|
|
400
361
|
return (double)seconds * 1000.0 + (double)nanoseconds / 1000000.0;
|
|
401
362
|
}
|
|
402
363
|
|
|
403
|
-
|
|
364
|
+
xt_value xt_node_stat_result(const xt_fs_stat_t *info) {
|
|
404
365
|
xt_value object = xt_object_new();
|
|
405
366
|
xt_object_set(object, xt_string_from_cstr("size"), xt_number((double)info->st_size));
|
|
406
367
|
xt_object_set(object, xt_string_from_cstr("mode"), xt_number((double)info->st_mode));
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* `fs.globSync` for xbintsc.
|
|
3
|
+
*
|
|
4
|
+
* A small, dependency-free globber supporting `*`, `?`, `[...]` character
|
|
5
|
+
* classes and the recursive `**` segment. Supported options: `cwd` and
|
|
6
|
+
* `withFileTypes`. `exclude` (a callback) and `follow` (symlinked directories)
|
|
7
|
+
* are not implemented (documented deviation).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "../node_common.h"
|
|
11
|
+
#include "fs_common.h"
|
|
12
|
+
|
|
13
|
+
typedef struct {
|
|
14
|
+
xt_value *items;
|
|
15
|
+
size_t count;
|
|
16
|
+
size_t capacity;
|
|
17
|
+
} xt_glob_list;
|
|
18
|
+
|
|
19
|
+
static void xt_glob_push(xt_glob_list *list, xt_value value) {
|
|
20
|
+
if (list->count == list->capacity) {
|
|
21
|
+
list->capacity = list->capacity ? list->capacity * 2 : 16;
|
|
22
|
+
xt_value *grown = (xt_value *)realloc(list->items, sizeof(xt_value) * list->capacity);
|
|
23
|
+
if (!grown) return;
|
|
24
|
+
list->items = grown;
|
|
25
|
+
}
|
|
26
|
+
list->items[list->count++] = value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static char *xt_glob_join(const char *dir, const char *name) {
|
|
30
|
+
size_t dirLength = strlen(dir);
|
|
31
|
+
int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/';
|
|
32
|
+
size_t size = dirLength + (needsSlash ? 1 : 0) + strlen(name) + 1;
|
|
33
|
+
char *path = (char *)malloc(size);
|
|
34
|
+
if (!path) return NULL;
|
|
35
|
+
snprintf(path, size, "%s%s%s", dir, needsSlash ? "/" : "", name);
|
|
36
|
+
return path;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Single path-segment glob match (`*`, `?`, `[...]`; no `/`). */
|
|
40
|
+
static int xt_glob_match_segment(const char *pattern, const char *text) {
|
|
41
|
+
while (*pattern) {
|
|
42
|
+
if (*pattern == '*') {
|
|
43
|
+
pattern++;
|
|
44
|
+
if (!*pattern) return 1;
|
|
45
|
+
while (*text) {
|
|
46
|
+
if (xt_glob_match_segment(pattern, text)) return 1;
|
|
47
|
+
text++;
|
|
48
|
+
}
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
if (*pattern == '?') {
|
|
52
|
+
if (!*text) return 0;
|
|
53
|
+
pattern++;
|
|
54
|
+
text++;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (*pattern == '[') {
|
|
58
|
+
const char *cursor = pattern + 1;
|
|
59
|
+
int negate = 0;
|
|
60
|
+
int matched = 0;
|
|
61
|
+
char value = *text;
|
|
62
|
+
if (*cursor == '!' || *cursor == '^') {
|
|
63
|
+
negate = 1;
|
|
64
|
+
cursor++;
|
|
65
|
+
}
|
|
66
|
+
while (*cursor && *cursor != ']') {
|
|
67
|
+
char low = *cursor++;
|
|
68
|
+
char high = low;
|
|
69
|
+
if (*cursor == '-' && cursor[1] && cursor[1] != ']') {
|
|
70
|
+
cursor++;
|
|
71
|
+
high = *cursor++;
|
|
72
|
+
}
|
|
73
|
+
if (value >= low && value <= high) matched = 1;
|
|
74
|
+
}
|
|
75
|
+
if (*cursor == ']') cursor++;
|
|
76
|
+
if (!*text || matched == negate) return 0;
|
|
77
|
+
pattern = cursor;
|
|
78
|
+
text++;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (*pattern != *text) return 0;
|
|
82
|
+
pattern++;
|
|
83
|
+
text++;
|
|
84
|
+
}
|
|
85
|
+
return *text == '\0';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* `follow` uses `stat` so a symlinked directory is traversed (needed for
|
|
89
|
+
* literal path segments such as macOS's `/tmp`); `**` uses `lstat` to match
|
|
90
|
+
* Node's default `follow: false` and avoid symlink cycles. */
|
|
91
|
+
static int xt_glob_is_dir(const char *path, int follow) {
|
|
92
|
+
xt_fs_stat_t info;
|
|
93
|
+
int failed = follow ? xt_fs_stat_fn(path, &info) : xt_fs_lstat_fn(path, &info);
|
|
94
|
+
if (failed != 0) return 0;
|
|
95
|
+
return S_ISDIR(info.st_mode);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static void xt_glob_walk(const char *dirPath, const char *pattern, const char *resultPrefix, int withFileTypes,
|
|
99
|
+
xt_glob_list *list);
|
|
100
|
+
|
|
101
|
+
/* Recurse one directory level for the `**` segment. */
|
|
102
|
+
static void xt_glob_descend(const char *dirPath, const char *pattern, const char *resultPrefix, int withFileTypes,
|
|
103
|
+
xt_glob_list *list) {
|
|
104
|
+
xt_fs_dir dir;
|
|
105
|
+
if (!xt_fs_dir_open(&dir, dirPath)) return;
|
|
106
|
+
while (xt_fs_dir_next(&dir)) {
|
|
107
|
+
const char *name = xt_fs_dir_name(&dir);
|
|
108
|
+
if (name[0] == '.') continue; /* dotfiles are not matched by default */
|
|
109
|
+
char *child = xt_glob_join(dirPath, name);
|
|
110
|
+
if (!child) continue;
|
|
111
|
+
if (xt_glob_is_dir(child, 0)) {
|
|
112
|
+
size_t size = strlen(resultPrefix) + strlen(name) + 2;
|
|
113
|
+
char *childPrefix = (char *)malloc(size);
|
|
114
|
+
if (childPrefix) {
|
|
115
|
+
snprintf(childPrefix, size, "%s%s/", resultPrefix, name);
|
|
116
|
+
xt_glob_walk(child, pattern, childPrefix, withFileTypes, list);
|
|
117
|
+
free(childPrefix);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
free(child);
|
|
121
|
+
}
|
|
122
|
+
xt_fs_dir_close(&dir);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static void xt_glob_walk(const char *dirPath, const char *pattern, const char *resultPrefix, int withFileTypes,
|
|
126
|
+
xt_glob_list *list) {
|
|
127
|
+
if (pattern[0] == '\0') {
|
|
128
|
+
if (resultPrefix[0] != '\0') {
|
|
129
|
+
size_t length = strlen(resultPrefix);
|
|
130
|
+
char *trimmed = (char *)malloc(length + 1);
|
|
131
|
+
if (!trimmed) return;
|
|
132
|
+
memcpy(trimmed, resultPrefix, length + 1);
|
|
133
|
+
while (length > 1 && trimmed[length - 1] == '/') trimmed[--length] = '\0';
|
|
134
|
+
xt_glob_push(list, xt_string_from_cstr(trimmed));
|
|
135
|
+
free(trimmed);
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const char *slash = strchr(pattern, '/');
|
|
141
|
+
size_t segmentLength = slash ? (size_t)(slash - pattern) : strlen(pattern);
|
|
142
|
+
const char *rest = slash ? slash + 1 : "";
|
|
143
|
+
|
|
144
|
+
if (segmentLength == 2 && pattern[0] == '*' && pattern[1] == '*') {
|
|
145
|
+
/* Zero directories: match the remainder here. */
|
|
146
|
+
xt_glob_walk(dirPath, rest, resultPrefix, withFileTypes, list);
|
|
147
|
+
/* One or more: descend and retry `**`. */
|
|
148
|
+
xt_glob_descend(dirPath, pattern, resultPrefix, withFileTypes, list);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
char segment[256];
|
|
153
|
+
if (segmentLength >= sizeof(segment)) return;
|
|
154
|
+
memcpy(segment, pattern, segmentLength);
|
|
155
|
+
segment[segmentLength] = '\0';
|
|
156
|
+
|
|
157
|
+
xt_fs_dir dir;
|
|
158
|
+
if (!xt_fs_dir_open(&dir, dirPath)) return;
|
|
159
|
+
while (xt_fs_dir_next(&dir)) {
|
|
160
|
+
const char *name = xt_fs_dir_name(&dir);
|
|
161
|
+
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue;
|
|
162
|
+
if (name[0] == '.' && segment[0] != '.') continue;
|
|
163
|
+
if (!xt_glob_match_segment(segment, name)) continue;
|
|
164
|
+
|
|
165
|
+
char *child = xt_glob_join(dirPath, name);
|
|
166
|
+
if (!child) continue;
|
|
167
|
+
if (rest[0] == '\0') {
|
|
168
|
+
if (withFileTypes) xt_glob_push(list, xt_node_dirent_result(dirPath, name));
|
|
169
|
+
else {
|
|
170
|
+
size_t size = strlen(resultPrefix) + strlen(name) + 1;
|
|
171
|
+
char *result = (char *)malloc(size);
|
|
172
|
+
if (result) {
|
|
173
|
+
snprintf(result, size, "%s%s", resultPrefix, name);
|
|
174
|
+
xt_glob_push(list, xt_string_from_cstr(result));
|
|
175
|
+
free(result);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} else if (xt_glob_is_dir(child, 1)) {
|
|
179
|
+
size_t size = strlen(resultPrefix) + strlen(name) + 2;
|
|
180
|
+
char *childPrefix = (char *)malloc(size);
|
|
181
|
+
if (childPrefix) {
|
|
182
|
+
snprintf(childPrefix, size, "%s%s/", resultPrefix, name);
|
|
183
|
+
xt_glob_walk(child, rest, childPrefix, withFileTypes, list);
|
|
184
|
+
free(childPrefix);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
free(child);
|
|
188
|
+
}
|
|
189
|
+
xt_fs_dir_close(&dir);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
static void xt_glob_run(const char *pattern, const char *cwd, int withFileTypes, xt_glob_list *list) {
|
|
193
|
+
if (!pattern) return;
|
|
194
|
+
const char *base = cwd && cwd[0] ? cwd : ".";
|
|
195
|
+
const char *prefix = "";
|
|
196
|
+
if (pattern[0] == '/') {
|
|
197
|
+
base = "/";
|
|
198
|
+
prefix = "/";
|
|
199
|
+
pattern++;
|
|
200
|
+
}
|
|
201
|
+
xt_glob_walk(base, pattern, prefix, withFileTypes, list);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
xt_value xt_node_glob(int32_t argc, xt_value *argv) {
|
|
205
|
+
if (argc < 1) return xt_array_new(0, NULL);
|
|
206
|
+
xt_value options = argc > 1 ? argv[1] : XT_UNDEFINED;
|
|
207
|
+
int withFileTypes = XT_IS_OBJECT(options) && xt_truthy(xt_object_get_cstr(options, "withFileTypes"));
|
|
208
|
+
const char *cwd = NULL;
|
|
209
|
+
xt_value cwdValue = XT_IS_OBJECT(options) ? xt_object_get_cstr(options, "cwd") : XT_UNDEFINED;
|
|
210
|
+
if (XT_IS_STRING(cwdValue)) cwd = xt_string_data(cwdValue);
|
|
211
|
+
|
|
212
|
+
xt_glob_list list;
|
|
213
|
+
list.items = NULL;
|
|
214
|
+
list.count = 0;
|
|
215
|
+
list.capacity = 0;
|
|
216
|
+
|
|
217
|
+
xt_value patternValue = argv[0];
|
|
218
|
+
if (XT_IS_ARRAY(patternValue)) {
|
|
219
|
+
int32_t count = (int32_t)xt_to_number(xt_array_length(patternValue));
|
|
220
|
+
for (int32_t i = 0; i < count; i++) {
|
|
221
|
+
xt_value item = xt_array_get(patternValue, xt_number((double)i));
|
|
222
|
+
if (XT_IS_STRING(item)) xt_glob_run(xt_string_data(item), cwd, withFileTypes, &list);
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
const char *pattern = xt_node_cstr(patternValue);
|
|
226
|
+
xt_glob_run(pattern, cwd, withFileTypes, &list);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
xt_value result = xt_array_new((int32_t)list.count, list.items);
|
|
230
|
+
free(list.items);
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Node.js `fs` symbolic/hard link operations for xbintsc.
|
|
3
|
+
*
|
|
4
|
+
* linkSync, symlinkSync, readlinkSync
|
|
5
|
+
*
|
|
6
|
+
* Windows uses `CreateHardLink`/`CreateSymbolicLink`; `readlinkSync` is not
|
|
7
|
+
* available there (documented deviation).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "../node_common.h"
|
|
11
|
+
#include "fs_common.h"
|
|
12
|
+
|
|
13
|
+
#if defined(_WIN32)
|
|
14
|
+
#include <windows.h>
|
|
15
|
+
#else
|
|
16
|
+
#include <unistd.h>
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
xt_value xt_node_link(int32_t argc, xt_value *argv) {
|
|
20
|
+
if (argc < 2) return xt_undefined();
|
|
21
|
+
const char *existing = xt_node_cstr(argv[0]);
|
|
22
|
+
const char *newPath = xt_node_cstr(argv[1]);
|
|
23
|
+
#if defined(_WIN32)
|
|
24
|
+
if (!existing || !newPath || !CreateHardLinkA(newPath, existing, NULL)) {
|
|
25
|
+
xt_fs_raise_errno(errno ? errno : EPERM, "link", newPath);
|
|
26
|
+
return xt_undefined();
|
|
27
|
+
}
|
|
28
|
+
#else
|
|
29
|
+
if (!existing || !newPath || link(existing, newPath) != 0) {
|
|
30
|
+
xt_fs_raise_path("link", newPath);
|
|
31
|
+
return xt_undefined();
|
|
32
|
+
}
|
|
33
|
+
#endif
|
|
34
|
+
return xt_undefined();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
xt_value xt_node_symlink(int32_t argc, xt_value *argv) {
|
|
38
|
+
if (argc < 2) return xt_undefined();
|
|
39
|
+
const char *target = xt_node_cstr(argv[0]);
|
|
40
|
+
const char *path = xt_node_cstr(argv[1]);
|
|
41
|
+
const char *type = argc > 2 ? xt_node_cstr(argv[2]) : NULL;
|
|
42
|
+
#if defined(_WIN32)
|
|
43
|
+
DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
|
|
44
|
+
if (type && (strcmp(type, "dir") == 0 || strcmp(type, "junction") == 0)) flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
|
|
45
|
+
if (!target || !path || !CreateSymbolicLinkA(path, target, flags)) {
|
|
46
|
+
xt_fs_raise_errno(errno ? errno : EPERM, "symlink", path);
|
|
47
|
+
return xt_undefined();
|
|
48
|
+
}
|
|
49
|
+
#else
|
|
50
|
+
(void)type;
|
|
51
|
+
if (!target || !path || symlink(target, path) != 0) {
|
|
52
|
+
xt_fs_raise_path("symlink", path);
|
|
53
|
+
return xt_undefined();
|
|
54
|
+
}
|
|
55
|
+
#endif
|
|
56
|
+
return xt_undefined();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
xt_value xt_node_readlink(int32_t argc, xt_value *argv) {
|
|
60
|
+
if (argc < 1) return xt_undefined();
|
|
61
|
+
const char *path = xt_node_cstr(argv[0]);
|
|
62
|
+
#if defined(_WIN32)
|
|
63
|
+
(void)path;
|
|
64
|
+
xt_fs_raise_errno(ENOSYS, "readlink", path);
|
|
65
|
+
return xt_undefined();
|
|
66
|
+
#else
|
|
67
|
+
if (!path) {
|
|
68
|
+
xt_fs_raise_errno(ENOENT, "readlink", NULL);
|
|
69
|
+
return xt_undefined();
|
|
70
|
+
}
|
|
71
|
+
size_t capacity = 256;
|
|
72
|
+
for (;;) {
|
|
73
|
+
char *buffer = (char *)malloc(capacity);
|
|
74
|
+
if (!buffer) return xt_undefined();
|
|
75
|
+
ssize_t length = readlink(path, buffer, capacity);
|
|
76
|
+
if (length < 0) {
|
|
77
|
+
free(buffer);
|
|
78
|
+
xt_fs_raise_path("readlink", path);
|
|
79
|
+
return xt_undefined();
|
|
80
|
+
}
|
|
81
|
+
if ((size_t)length < capacity) {
|
|
82
|
+
buffer[length] = '\0';
|
|
83
|
+
xt_value result = xt_string_from_cstr(buffer);
|
|
84
|
+
free(buffer);
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
free(buffer);
|
|
88
|
+
capacity *= 2;
|
|
89
|
+
}
|
|
90
|
+
#endif
|
|
91
|
+
}
|