Skip to content

Commit 1b2a339

Browse files
committed
FS: introduced fs.readlinkSync() and friends.
This closes #802 feature request on Github.
1 parent 1985390 commit 1b2a339

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed

external/njs_fs_module.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ static njs_int_t njs_fs_read_file(njs_vm_t *vm, njs_value_t *args,
160160
njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval);
161161
static njs_int_t njs_fs_readdir(njs_vm_t *vm, njs_value_t *args,
162162
njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval);
163+
static njs_int_t njs_fs_readlink(njs_vm_t *vm, njs_value_t *args,
164+
njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval);
163165
static njs_int_t njs_fs_realpath(njs_vm_t *vm, njs_value_t *args,
164166
njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval);
165167
static njs_int_t njs_fs_rename(njs_vm_t *vm, njs_value_t *args,
@@ -415,6 +417,17 @@ static njs_external_t njs_ext_fs_promises[] = {
415417
}
416418
},
417419

420+
{
421+
.flags = NJS_EXTERN_METHOD,
422+
.name.string = njs_str("readlink"),
423+
.writable = 1,
424+
.configurable = 1,
425+
.u.method = {
426+
.native = njs_fs_readlink,
427+
.magic8 = NJS_FS_PROMISE,
428+
}
429+
},
430+
418431
{
419432
.flags = NJS_EXTERN_METHOD,
420433
.name.string = njs_str("realpath"),
@@ -726,6 +739,28 @@ static njs_external_t njs_ext_fs[] = {
726739
}
727740
},
728741

742+
{
743+
.flags = NJS_EXTERN_METHOD,
744+
.name.string = njs_str("readlink"),
745+
.writable = 1,
746+
.configurable = 1,
747+
.u.method = {
748+
.native = njs_fs_readlink,
749+
.magic8 = NJS_FS_CALLBACK,
750+
}
751+
},
752+
753+
{
754+
.flags = NJS_EXTERN_METHOD,
755+
.name.string = njs_str("readlinkSync"),
756+
.writable = 1,
757+
.configurable = 1,
758+
.u.method = {
759+
.native = njs_fs_readlink,
760+
.magic8 = NJS_FS_DIRECT,
761+
}
762+
},
763+
729764
{
730765
.flags = NJS_EXTERN_METHOD,
731766
.name.string = njs_str("realpath"),
@@ -2035,6 +2070,99 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
20352070
}
20362071

20372072

2073+
static njs_int_t
2074+
njs_fs_readlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2075+
njs_index_t calltype, njs_value_t *retval)
2076+
{
2077+
ssize_t n;
2078+
njs_int_t ret;
2079+
njs_str_t s;
2080+
const char *path;
2081+
njs_value_t *callback, *options;
2082+
njs_opaque_value_t encode, result;
2083+
const njs_buffer_encoding_t *encoding;
2084+
char path_buf[NJS_MAX_PATH + 1],
2085+
dst_buf[NJS_MAX_PATH + 1];
2086+
2087+
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
2088+
if (njs_slow_path(path == NULL)) {
2089+
return NJS_ERROR;
2090+
}
2091+
2092+
callback = NULL;
2093+
options = njs_arg(args, nargs, 2);
2094+
2095+
if (calltype == NJS_FS_CALLBACK) {
2096+
callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
2097+
if (!njs_value_is_function(callback)) {
2098+
njs_vm_type_error(vm, "\"callback\" must be a function");
2099+
return NJS_ERROR;
2100+
}
2101+
2102+
if (options == callback) {
2103+
options = njs_value_arg(&njs_value_undefined);
2104+
}
2105+
}
2106+
2107+
njs_value_undefined_set(njs_value_arg(&encode));
2108+
2109+
if (njs_value_is_string(options)) {
2110+
njs_value_assign(&encode, options);
2111+
2112+
} else if (!njs_value_is_undefined(options)) {
2113+
if (!njs_value_is_object(options)) {
2114+
njs_vm_type_error(vm, "Unknown options type "
2115+
"(a string or object required)");
2116+
return NJS_ERROR;
2117+
}
2118+
2119+
(void) njs_vm_object_prop(vm, options, &string_encoding, &encode);
2120+
}
2121+
2122+
encoding = NULL;
2123+
2124+
if (njs_value_is_string(njs_value_arg(&encode))) {
2125+
njs_value_string_get(njs_value_arg(&encode), &s);
2126+
2127+
} else {
2128+
s.length = 0;
2129+
s.start = NULL;
2130+
}
2131+
2132+
if (!njs_strstr_eq(&s, &string_buffer)) {
2133+
encoding = njs_buffer_encoding(vm, njs_value_arg(&encode), 1);
2134+
if (njs_slow_path(encoding == NULL)) {
2135+
return NJS_ERROR;
2136+
}
2137+
}
2138+
2139+
s.start = (u_char *) dst_buf;
2140+
n = readlink(path, dst_buf, sizeof(dst_buf) - 1);
2141+
if (njs_slow_path(n < 0)) {
2142+
ret = njs_fs_error(vm, "readlink", strerror(errno), path, errno,
2143+
&result);
2144+
goto done;
2145+
}
2146+
2147+
s.length = n;
2148+
2149+
if (encoding == NULL) {
2150+
ret = njs_buffer_new(vm, njs_value_arg(&result), s.start, s.length);
2151+
2152+
} else {
2153+
ret = encoding->encode(vm, njs_value_arg(&result), &s);
2154+
}
2155+
2156+
done:
2157+
2158+
if (ret == NJS_OK) {
2159+
return njs_fs_result(vm, &result, calltype, callback, 2, retval);
2160+
}
2161+
2162+
return NJS_ERROR;
2163+
}
2164+
2165+
20382166
static njs_int_t
20392167
njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
20402168
njs_index_t calltype, njs_value_t *retval)

test/fs/methods.t.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,56 @@ let realpathP_tsuite = {
435435
get tests() { return realpath_tests() },
436436
};
437437

438+
async function readlink_test(params) {
439+
let lname = params.args[0];
440+
try { fs.unlinkSync(lname); } catch (e) {}
441+
fs.symlinkSync("test/fs/ascii", lname);
442+
443+
let data = await method("readlink", params);
444+
445+
if (!params.check(data)) {
446+
throw Error(`readlink failed check`);
447+
}
448+
449+
return 'SUCCESS';
450+
}
451+
452+
let readlink_tests = () => [
453+
{ args: [`${test_dir}/symlink`],
454+
check: (data) => data.endsWith("test/fs/ascii") },
455+
{ args: [`${test_dir}/symlink`, {encoding:'buffer'}],
456+
check: (data) => data instanceof Buffer },
457+
{ args: [`${test_dir}/symlink`, {encoding:'hex'}],
458+
check: (data) => data.endsWith("746573742f66732f6173636969") },
459+
];
460+
461+
let readlink_tsuite = {
462+
name: "fs readlink",
463+
skip: () => (!has_fs() || !has_buffer()),
464+
T: readlink_test,
465+
prepare_args: p,
466+
opts: { type: "callback" },
467+
get tests() { return readlink_tests() },
468+
};
469+
470+
let readlinkSync_tsuite = {
471+
name: "fs readlinkSync",
472+
skip: () => (!has_fs() || !has_buffer()),
473+
T: readlink_test,
474+
prepare_args: p,
475+
opts: { type: "sync" },
476+
get tests() { return readlink_tests() },
477+
};
478+
479+
let readlinkP_tsuite = {
480+
name: "fsp readlink",
481+
skip: () => (!has_fs() || !has_buffer()),
482+
T: readlink_test,
483+
prepare_args: p,
484+
opts: { type: "promise" },
485+
get tests() { return readlink_tests() },
486+
};
487+
438488
async function method_test(params) {
439489
if (params.init) {
440490
params.init(params);
@@ -1190,6 +1240,9 @@ run([
11901240
realpath_tsuite,
11911241
realpathSync_tsuite,
11921242
realpathP_tsuite,
1243+
readlink_tsuite,
1244+
readlinkSync_tsuite,
1245+
readlinkP_tsuite,
11931246
stat_tsuite,
11941247
statSync_tsuite,
11951248
statP_tsuite,

0 commit comments

Comments
 (0)