Skip to content

Commit 060e5f0

Browse files
committed
fs: Buffer and encoding enhancements to fs API
This makes several changes: 1. Allow path/filename to be passed in as a Buffer on fs methods 2. Add `options.encoding` to fs.readdir, fs.readdirSync, fs.readlink, fs.readlinkSync and fs.watch. 3. Documentation updates For 1... it's now possible to do: ```js fs.open(Buffer('/fs/foo/bar'), 'w+', (err, fd) => { }); ``` For 2... ```js fs.readdir('/fs/foo/bar', {encoding:'hex'}, (err,list) => { }); fs.readdir('/fs/foo/bar', {encoding:'buffer'}, (err, list) => { }); ``` encoding can also be passed as a string ```js fs.readdir('/fs/foo/bar', 'hex', (err,list) => { }); ``` The default encoding is set to UTF8 so this addresses the discrepency that existed previously between fs.readdir and fs.watch handling filenames differently. Fixes: #2088 Refs: #3519 PR-URL: #5616 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Trevor Norris <[email protected]>
1 parent 4d4f353 commit 060e5f0

15 files changed

+654
-228
lines changed

doc/api/fs.markdown

+129-60
Large diffs are not rendered by default.

lib/fs.js

+79-27
Original file line numberDiff line numberDiff line change
@@ -903,17 +903,32 @@ fs.mkdirSync = function(path, mode) {
903903
modeNum(mode, 0o777));
904904
};
905905

906-
fs.readdir = function(path, callback) {
906+
fs.readdir = function(path, options, callback) {
907+
options = options || {};
908+
if (typeof options === 'function') {
909+
callback = options;
910+
options = {};
911+
} else if (typeof options === 'string') {
912+
options = {encoding: options};
913+
}
914+
if (typeof options !== 'object')
915+
throw new TypeError('"options" must be a string or an object');
916+
907917
callback = makeCallback(callback);
908918
if (!nullCheck(path, callback)) return;
909919
var req = new FSReqWrap();
910920
req.oncomplete = callback;
911-
binding.readdir(pathModule._makeLong(path), req);
921+
binding.readdir(pathModule._makeLong(path), options.encoding, req);
912922
};
913923

914-
fs.readdirSync = function(path) {
924+
fs.readdirSync = function(path, options) {
925+
options = options || {};
926+
if (typeof options === 'string')
927+
options = {encoding: options};
928+
if (typeof options !== 'object')
929+
throw new TypeError('"options" must be a string or an object');
915930
nullCheck(path);
916-
return binding.readdir(pathModule._makeLong(path));
931+
return binding.readdir(pathModule._makeLong(path), options.encoding);
917932
};
918933

919934
fs.fstat = function(fd, callback) {
@@ -952,17 +967,31 @@ fs.statSync = function(path) {
952967
return binding.stat(pathModule._makeLong(path));
953968
};
954969

955-
fs.readlink = function(path, callback) {
970+
fs.readlink = function(path, options, callback) {
971+
options = options || {};
972+
if (typeof options === 'function') {
973+
callback = options;
974+
options = {};
975+
} else if (typeof options === 'string') {
976+
options = {encoding: options};
977+
}
978+
if (typeof options !== 'object')
979+
throw new TypeError('"options" must be a string or an object');
956980
callback = makeCallback(callback);
957981
if (!nullCheck(path, callback)) return;
958982
var req = new FSReqWrap();
959983
req.oncomplete = callback;
960-
binding.readlink(pathModule._makeLong(path), req);
984+
binding.readlink(pathModule._makeLong(path), options.encoding, req);
961985
};
962986

963-
fs.readlinkSync = function(path) {
987+
fs.readlinkSync = function(path, options) {
988+
options = options || {};
989+
if (typeof options === 'string')
990+
options = {encoding: options};
991+
if (typeof options !== 'object')
992+
throw new TypeError('"options" must be a string or an object');
964993
nullCheck(path);
965-
return binding.readlink(pathModule._makeLong(path));
994+
return binding.readlink(pathModule._makeLong(path), options.encoding);
966995
};
967996

968997
function preprocessSymlinkDestination(path, type, linkPath) {
@@ -1363,11 +1392,15 @@ function FSWatcher() {
13631392
}
13641393
util.inherits(FSWatcher, EventEmitter);
13651394

1366-
FSWatcher.prototype.start = function(filename, persistent, recursive) {
1395+
FSWatcher.prototype.start = function(filename,
1396+
persistent,
1397+
recursive,
1398+
encoding) {
13671399
nullCheck(filename);
13681400
var err = this._handle.start(pathModule._makeLong(filename),
13691401
persistent,
1370-
recursive);
1402+
recursive,
1403+
encoding);
13711404
if (err) {
13721405
this._handle.close();
13731406
const error = errnoException(err, `watch ${filename}`);
@@ -1380,25 +1413,27 @@ FSWatcher.prototype.close = function() {
13801413
this._handle.close();
13811414
};
13821415

1383-
fs.watch = function(filename) {
1416+
fs.watch = function(filename, options, listener) {
13841417
nullCheck(filename);
1385-
var watcher;
1386-
var options;
1387-
var listener;
13881418

1389-
if (arguments[1] !== null && typeof arguments[1] === 'object') {
1390-
options = arguments[1];
1391-
listener = arguments[2];
1392-
} else {
1419+
options = options || {};
1420+
if (typeof options === 'function') {
1421+
listener = options;
13931422
options = {};
1394-
listener = arguments[1];
1423+
} else if (typeof options === 'string') {
1424+
options = {encoding: options};
13951425
}
1426+
if (typeof options !== 'object')
1427+
throw new TypeError('"options" must be a string or an object');
13961428

13971429
if (options.persistent === undefined) options.persistent = true;
13981430
if (options.recursive === undefined) options.recursive = false;
13991431

1400-
watcher = new FSWatcher();
1401-
watcher.start(filename, options.persistent, options.recursive);
1432+
const watcher = new FSWatcher();
1433+
watcher.start(filename,
1434+
options.persistent,
1435+
options.recursive,
1436+
options.encoding);
14021437

14031438
if (listener) {
14041439
watcher.addListener('change', listener);
@@ -2139,10 +2174,19 @@ SyncWriteStream.prototype.destroy = function() {
21392174

21402175
SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy;
21412176

2142-
fs.mkdtemp = function(prefix, callback) {
2143-
if (typeof callback !== 'function') {
2144-
throw new TypeError('"callback" argument must be a function');
2177+
fs.mkdtemp = function(prefix, options, callback) {
2178+
if (!prefix || typeof prefix !== 'string')
2179+
throw new TypeError('filename prefix is required');
2180+
2181+
options = options || {};
2182+
if (typeof options === 'function') {
2183+
callback = options;
2184+
options = {};
2185+
} else if (typeof options === 'string') {
2186+
options = {encoding: options};
21452187
}
2188+
if (typeof options !== 'object')
2189+
throw new TypeError('"options" must be a string or an object');
21462190

21472191
if (!nullCheck(prefix, callback)) {
21482192
return;
@@ -2151,11 +2195,19 @@ fs.mkdtemp = function(prefix, callback) {
21512195
var req = new FSReqWrap();
21522196
req.oncomplete = callback;
21532197

2154-
binding.mkdtemp(prefix + 'XXXXXX', req);
2198+
binding.mkdtemp(prefix + 'XXXXXX', options.encoding, req);
21552199
};
21562200

2157-
fs.mkdtempSync = function(prefix) {
2201+
fs.mkdtempSync = function(prefix, options) {
2202+
if (!prefix || typeof prefix !== 'string')
2203+
throw new TypeError('filename prefix is required');
2204+
2205+
options = options || {};
2206+
if (typeof options === 'string')
2207+
options = {encoding: options};
2208+
if (typeof options !== 'object')
2209+
throw new TypeError('"options" must be a string or an object');
21582210
nullCheck(prefix);
21592211

2160-
return binding.mkdtemp(prefix + 'XXXXXX');
2212+
return binding.mkdtemp(prefix + 'XXXXXX', options.encoding);
21612213
};

src/fs_event_wrap.cc

+22-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "util-inl.h"
77
#include "node.h"
88
#include "handle_wrap.h"
9+
#include "string_bytes.h"
910

1011
#include <stdlib.h>
1112

@@ -41,6 +42,7 @@ class FSEventWrap: public HandleWrap {
4142

4243
uv_fs_event_t handle_;
4344
bool initialized_;
45+
enum encoding encoding_;
4446
};
4547

4648

@@ -86,16 +88,20 @@ void FSEventWrap::Start(const FunctionCallbackInfo<Value>& args) {
8688

8789
FSEventWrap* wrap = Unwrap<FSEventWrap>(args.Holder());
8890

89-
if (args.Length() < 1 || !args[0]->IsString()) {
90-
return env->ThrowTypeError("filename must be a valid string");
91-
}
91+
static const char kErrMsg[] = "filename must be a string or Buffer";
92+
if (args.Length() < 1)
93+
return env->ThrowTypeError(kErrMsg);
9294

93-
node::Utf8Value path(env->isolate(), args[0]);
95+
BufferValue path(env->isolate(), args[0]);
96+
if (*path == nullptr)
97+
return env->ThrowTypeError(kErrMsg);
9498

9599
unsigned int flags = 0;
96100
if (args[2]->IsTrue())
97101
flags |= UV_FS_EVENT_RECURSIVE;
98102

103+
wrap->encoding_ = ParseEncoding(env->isolate(), args[3], UTF8);
104+
99105
int err = uv_fs_event_init(wrap->env()->event_loop(), &wrap->handle_);
100106
if (err == 0) {
101107
wrap->initialized_ = true;
@@ -156,7 +162,18 @@ void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename,
156162
};
157163

158164
if (filename != nullptr) {
159-
argv[2] = OneByteString(env->isolate(), filename);
165+
Local<Value> fn = StringBytes::Encode(env->isolate(),
166+
filename,
167+
wrap->encoding_);
168+
if (fn.IsEmpty()) {
169+
argv[0] = Integer::New(env->isolate(), UV_EINVAL);
170+
argv[2] = StringBytes::Encode(env->isolate(),
171+
filename,
172+
strlen(filename),
173+
BUFFER);
174+
} else {
175+
argv[2] = fn;
176+
}
160177
}
161178

162179
wrap->MakeCallback(env->onchange_string(), ARRAY_SIZE(argv), argv);

0 commit comments

Comments
 (0)