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: https://github.com/nodejs/node/issues/2088 Refs: https://github.com/nodejs/node/issues/3519 PR-URL: https://github.com/nodejs/node/pull/5616 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Trevor Norris <trev.norris@gmail.com>
This commit is contained in:
Родитель
4d4f3535a9
Коммит
060e5f0c00
|
@ -101,11 +101,24 @@ Objects returned from `fs.watch()` are of this type.
|
|||
### Event: 'change'
|
||||
|
||||
* `event` {String} The type of fs change
|
||||
* `filename` {String} The filename that changed (if relevant/available)
|
||||
* `filename` {String | Buffer} The filename that changed (if relevant/available)
|
||||
|
||||
Emitted when something changes in a watched directory or file.
|
||||
See more details in [`fs.watch()`][].
|
||||
|
||||
The `filename` argument may not be provided depending on operating system
|
||||
support. If `filename` is provided, it will be provided as a `Buffer` if
|
||||
`fs.watch()` is called with it's `encoding` option set to `'buffer'`, otherwise
|
||||
`filename` will be a string.
|
||||
|
||||
```js
|
||||
fs.watch('./tmp', {encoding: 'buffer'}, (event, filename) => {
|
||||
if (filename)
|
||||
console.log(filename);
|
||||
// Prints: <Buffer ...>
|
||||
});
|
||||
```
|
||||
|
||||
### Event: 'error'
|
||||
|
||||
* `error` {Error}
|
||||
|
@ -128,7 +141,10 @@ Emitted when the ReadStream's file is opened.
|
|||
|
||||
### readStream.path
|
||||
|
||||
The path to the file the stream is reading from.
|
||||
The path to the file the stream is reading from as specified in the first
|
||||
argument to `fs.createReadStream()`. If `path` is passed as a string, then
|
||||
`readStream.path` will be a string. If `path` is passed as a `Buffer`, then
|
||||
`readStream.path` will be a `Buffer`.
|
||||
|
||||
## Class: fs.Stats
|
||||
|
||||
|
@ -217,11 +233,14 @@ for writing.
|
|||
|
||||
### writeStream.path
|
||||
|
||||
The path to the file the stream is writing to.
|
||||
The path to the file the stream is writing to as specified in the first
|
||||
argument to `fs.createWriteStream()`. If `path` is passed as a string, then
|
||||
`writeStream.path` will be a string. If `path` is passed as a `Buffer`, then
|
||||
`writeStream.path` will be a `Buffer`.
|
||||
|
||||
## fs.access(path[, mode], callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `mode` {Integer}
|
||||
* `callback` {Function}
|
||||
|
||||
|
@ -251,7 +270,7 @@ fs.access('/etc/passwd', fs.R_OK | fs.W_OK, (err) => {
|
|||
|
||||
## fs.accessSync(path[, mode])
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `mode` {Integer}
|
||||
|
||||
Synchronous version of [`fs.access()`][]. This throws if any accessibility checks
|
||||
|
@ -259,7 +278,7 @@ fail, and does nothing otherwise.
|
|||
|
||||
## fs.appendFile(file, data[, options], callback)
|
||||
|
||||
* `file` {String | Number} filename or file descriptor
|
||||
* `file` {String | Buffer | Number} filename or file descriptor
|
||||
* `data` {String | Buffer}
|
||||
* `options` {Object | String}
|
||||
* `encoding` {String | Null} default = `'utf8'`
|
||||
|
@ -291,11 +310,18 @@ _Note: Specified file descriptors will not be closed automatically._
|
|||
|
||||
## fs.appendFileSync(file, data[, options])
|
||||
|
||||
* `file` {String | Buffer}
|
||||
* `data` {String | Buffer}
|
||||
* `options` {Object | String}
|
||||
* `encoding` {String | Null} default = `'utf8'`
|
||||
* `mode` {Integer} default = `0o666`
|
||||
* `flag` {String} default = `'a'`
|
||||
|
||||
The synchronous version of [`fs.appendFile()`][]. Returns `undefined`.
|
||||
|
||||
## fs.chmod(path, mode, callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `mode` {Integer}
|
||||
* `callback` {Function}
|
||||
|
||||
|
@ -304,14 +330,14 @@ to the completion callback.
|
|||
|
||||
## fs.chmodSync(path, mode)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `mode` {Integer}
|
||||
|
||||
Synchronous chmod(2). Returns `undefined`.
|
||||
|
||||
## fs.chown(path, uid, gid, callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `uid` {Integer}
|
||||
* `gid` {Integer}
|
||||
* `callback` {Function}
|
||||
|
@ -321,7 +347,7 @@ to the completion callback.
|
|||
|
||||
## fs.chownSync(path, uid, gid)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `uid` {Integer}
|
||||
* `gid` {Integer}
|
||||
|
||||
|
@ -343,7 +369,7 @@ Synchronous close(2). Returns `undefined`.
|
|||
|
||||
## fs.createReadStream(path[, options])
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `options` {String | Object}
|
||||
* `flags` {String}
|
||||
* `encoding` {String}
|
||||
|
@ -399,7 +425,7 @@ If `options` is a string, then it specifies the encoding.
|
|||
|
||||
## fs.createWriteStream(path[, options])
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `options` {String | Object}
|
||||
* `flags` {String}
|
||||
* `defaultEncoding` {String}
|
||||
|
@ -444,7 +470,7 @@ If `options` is a string, then it specifies the encoding.
|
|||
|
||||
Stability: 0 - Deprecated: Use [`fs.stat()`][] or [`fs.access()`][] instead.
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `callback` {Function}
|
||||
|
||||
Test whether or not the given path exists by checking with the file system.
|
||||
|
@ -466,7 +492,7 @@ non-existent.
|
|||
|
||||
Stability: 0 - Deprecated: Use [`fs.statSync()`][] or [`fs.accessSync()`][] instead.
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
|
||||
Synchronous version of [`fs.exists()`][].
|
||||
Returns `true` if the file exists, `false` otherwise.
|
||||
|
@ -584,7 +610,7 @@ Synchronous version of [`fs.futimes()`][]. Returns `undefined`.
|
|||
|
||||
## fs.lchmod(path, mode, callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `mode` {Integer}
|
||||
* `callback` {Function}
|
||||
|
||||
|
@ -595,14 +621,14 @@ Only available on Mac OS X.
|
|||
|
||||
## fs.lchmodSync(path, mode)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `mode` {Integer}
|
||||
|
||||
Synchronous lchmod(2). Returns `undefined`.
|
||||
|
||||
## fs.lchown(path, uid, gid, callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `uid` {Integer}
|
||||
* `gid` {Integer}
|
||||
* `callback` {Function}
|
||||
|
@ -612,7 +638,7 @@ to the completion callback.
|
|||
|
||||
## fs.lchownSync(path, uid, gid)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `uid` {Integer}
|
||||
* `gid` {Integer}
|
||||
|
||||
|
@ -620,8 +646,8 @@ Synchronous lchown(2). Returns `undefined`.
|
|||
|
||||
## fs.link(srcpath, dstpath, callback)
|
||||
|
||||
* `srcpath` {String}
|
||||
* `dstpath` {String}
|
||||
* `srcpath` {String | Buffer}
|
||||
* `dstpath` {String | Buffer}
|
||||
* `callback` {Function}
|
||||
|
||||
Asynchronous link(2). No arguments other than a possible exception are given to
|
||||
|
@ -629,14 +655,14 @@ the completion callback.
|
|||
|
||||
## fs.linkSync(srcpath, dstpath)
|
||||
|
||||
* `srcpath` {String}
|
||||
* `dstpath` {String}
|
||||
* `srcpath` {String | Buffer}
|
||||
* `dstpath` {String | Buffer}
|
||||
|
||||
Synchronous link(2). Returns `undefined`.
|
||||
|
||||
## fs.lstat(path, callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `callback` {Function}
|
||||
|
||||
Asynchronous lstat(2). The callback gets two arguments `(err, stats)` where
|
||||
|
@ -646,13 +672,13 @@ refers to.
|
|||
|
||||
## fs.lstatSync(path)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
|
||||
Synchronous lstat(2). Returns an instance of `fs.Stats`.
|
||||
|
||||
## fs.mkdir(path[, mode], callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `mode` {Integer}
|
||||
* `callback` {Function}
|
||||
|
||||
|
@ -661,7 +687,7 @@ to the completion callback. `mode` defaults to `0o777`.
|
|||
|
||||
## fs.mkdirSync(path[, mode])
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `mode` {Integer}
|
||||
|
||||
Synchronous mkdir(2). Returns `undefined`.
|
||||
|
@ -692,7 +718,7 @@ folder path.
|
|||
|
||||
## fs.open(path, flags[, mode], callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `flags` {String | Number}
|
||||
* `mode` {Integer}
|
||||
* `callback` {Function}
|
||||
|
@ -759,7 +785,7 @@ the end of the file.
|
|||
|
||||
## fs.openSync(path, flags[, mode])
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `flags` {String | Number}
|
||||
* `mode` {Integer}
|
||||
|
||||
|
@ -788,7 +814,12 @@ If `position` is `null`, data will be read from the current file position.
|
|||
|
||||
The callback is given the three arguments, `(err, bytesRead, buffer)`.
|
||||
|
||||
## fs.readdir(path, callback)
|
||||
## fs.readdir(path[, options], callback)
|
||||
|
||||
* `path` {String | Buffer}
|
||||
* `options` {String | Object}
|
||||
* `encoding` {String} default = `'utf8'`
|
||||
* `callback` {Function}
|
||||
|
||||
* `path` {String}
|
||||
* `callback` {Function}
|
||||
|
@ -797,16 +828,30 @@ Asynchronous readdir(3). Reads the contents of a directory.
|
|||
The callback gets two arguments `(err, files)` where `files` is an array of
|
||||
the names of the files in the directory excluding `'.'` and `'..'`.
|
||||
|
||||
## fs.readdirSync(path)
|
||||
The optional `options` argument can be a string specifying an encoding, or an
|
||||
object with an `encoding` property specifying the character encoding to use for
|
||||
the filenames passed to the callback. If the `encoding` is set to `'buffer'`,
|
||||
the filenames returned will be passed as `Buffer` objects.
|
||||
|
||||
## fs.readdirSync(path[, options])
|
||||
|
||||
* `path` {String | Buffer}
|
||||
* `options` {String | Object}
|
||||
* `encoding` {String} default = `'utf8'`
|
||||
|
||||
* `path` {String}
|
||||
|
||||
Synchronous readdir(3). Returns an array of filenames excluding `'.'` and
|
||||
`'..'`.
|
||||
|
||||
The optional `options` argument can be a string specifying an encoding, or an
|
||||
object with an `encoding` property specifying the character encoding to use for
|
||||
the filenames passed to the callback. If the `encoding` is set to `'buffer'`,
|
||||
the filenames returned will be passed as `Buffer` objects.
|
||||
|
||||
## fs.readFile(file[, options], callback)
|
||||
|
||||
* `file` {String | Integer} filename or file descriptor
|
||||
* `file` {String | Buffer | Integer} filename or file descriptor
|
||||
* `options` {Object | String}
|
||||
* `encoding` {String | Null} default = `null`
|
||||
* `flag` {String} default = `'r'`
|
||||
|
@ -838,7 +883,7 @@ _Note: Specified file descriptors will not be closed automatically._
|
|||
|
||||
## fs.readFileSync(file[, options])
|
||||
|
||||
* `file` {String | Integer} filename or file descriptor
|
||||
* `file` {String | Buffer | Integer} filename or file descriptor
|
||||
* `options` {Object | String}
|
||||
* `encoding` {String | Null} default = `null`
|
||||
* `flag` {String} default = `'r'`
|
||||
|
@ -848,7 +893,12 @@ Synchronous version of [`fs.readFile`][]. Returns the contents of the `file`.
|
|||
If the `encoding` option is specified then this function returns a
|
||||
string. Otherwise it returns a buffer.
|
||||
|
||||
## fs.readlink(path, callback)
|
||||
## fs.readlink(path[, options], callback)
|
||||
|
||||
* `path` {String | Buffer}
|
||||
* `options` {String | Object}
|
||||
* `encoding` {String} default = `'utf8'`
|
||||
* `callback` {Function}
|
||||
|
||||
* `path` {String}
|
||||
* `callback` {Function}
|
||||
|
@ -856,15 +906,29 @@ string. Otherwise it returns a buffer.
|
|||
Asynchronous readlink(2). The callback gets two arguments `(err,
|
||||
linkString)`.
|
||||
|
||||
## fs.readlinkSync(path)
|
||||
The optional `options` argument can be a string specifying an encoding, or an
|
||||
object with an `encoding` property specifying the character encoding to use for
|
||||
the link path passed to the callback. If the `encoding` is set to `'buffer'`,
|
||||
the link path returned will be passed as a `Buffer` object.
|
||||
|
||||
## fs.readlinkSync(path[, options])
|
||||
|
||||
* `path` {String | Buffer}
|
||||
* `options` {String | Object}
|
||||
* `encoding` {String} default = `'utf8'`
|
||||
|
||||
* `path` {String}
|
||||
|
||||
Synchronous readlink(2). Returns the symbolic link's string value.
|
||||
|
||||
The optional `options` argument can be a string specifying an encoding, or an
|
||||
object with an `encoding` property specifying the character encoding to use for
|
||||
the link path passed to the callback. If the `encoding` is set to `'buffer'`,
|
||||
the link path returned will be passed as a `Buffer` object.
|
||||
|
||||
## fs.realpath(path[, cache], callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `cache` {Object}
|
||||
* `callback` {Function}
|
||||
|
||||
|
@ -895,7 +959,7 @@ Synchronous version of [`fs.read()`][]. Returns the number of `bytesRead`.
|
|||
|
||||
## fs.realpathSync(path[, cache])
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer};
|
||||
* `cache` {Object}
|
||||
|
||||
Synchronous realpath(2). Returns the resolved path. `cache` is an
|
||||
|
@ -904,8 +968,8 @@ resolution or avoid additional `fs.stat` calls for known real paths.
|
|||
|
||||
## fs.rename(oldPath, newPath, callback)
|
||||
|
||||
* `oldPath` {String}
|
||||
* `newPath` {String}
|
||||
* `oldPath` {String | Buffer}
|
||||
* `newPath` {String | Buffer}
|
||||
* `callback` {Function}
|
||||
|
||||
Asynchronous rename(2). No arguments other than a possible exception are given
|
||||
|
@ -913,14 +977,14 @@ to the completion callback.
|
|||
|
||||
## fs.renameSync(oldPath, newPath)
|
||||
|
||||
* `oldPath` {String}
|
||||
* `newPath` {String}
|
||||
* `oldPath` {String | Buffer}
|
||||
* `newPath` {String | Buffer}
|
||||
|
||||
Synchronous rename(2). Returns `undefined`.
|
||||
|
||||
## fs.rmdir(path, callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `callback` {Function}
|
||||
|
||||
Asynchronous rmdir(2). No arguments other than a possible exception are given
|
||||
|
@ -928,13 +992,13 @@ to the completion callback.
|
|||
|
||||
## fs.rmdirSync(path)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
|
||||
Synchronous rmdir(2). Returns `undefined`.
|
||||
|
||||
## fs.stat(path, callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `callback` {Function}
|
||||
|
||||
Asynchronous stat(2). The callback gets two arguments `(err, stats)` where
|
||||
|
@ -943,14 +1007,14 @@ information.
|
|||
|
||||
## fs.statSync(path)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
|
||||
Synchronous stat(2). Returns an instance of [`fs.Stats`][].
|
||||
|
||||
## fs.symlink(target, path[, type], callback)
|
||||
|
||||
* `target` {String}
|
||||
* `path` {String}
|
||||
* `target` {String | Buffer}
|
||||
* `path` {String | Buffer}
|
||||
* `type` {String}
|
||||
* `callback` {Function}
|
||||
|
||||
|
@ -971,15 +1035,15 @@ It creates a symbolic link named "new-port" that points to "foo".
|
|||
|
||||
## fs.symlinkSync(target, path[, type])
|
||||
|
||||
* `target` {String}
|
||||
* `path` {String}
|
||||
* `target` {String | Buffer}
|
||||
* `path` {String | Buffer}
|
||||
* `type` {String}
|
||||
|
||||
Synchronous symlink(2). Returns `undefined`.
|
||||
|
||||
## fs.truncate(path, len, callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `len` {Integer}
|
||||
* `callback` {Function}
|
||||
|
||||
|
@ -989,14 +1053,14 @@ first argument. In this case, `fs.ftruncate()` is called.
|
|||
|
||||
## fs.truncateSync(path, len)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `len` {Integer}
|
||||
|
||||
Synchronous truncate(2). Returns `undefined`.
|
||||
|
||||
## fs.unlink(path, callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `callback` {Function}
|
||||
|
||||
Asynchronous unlink(2). No arguments other than a possible exception are given
|
||||
|
@ -1004,13 +1068,13 @@ to the completion callback.
|
|||
|
||||
## fs.unlinkSync(path)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
|
||||
Synchronous unlink(2). Returns `undefined`.
|
||||
|
||||
## fs.unwatchFile(filename[, listener])
|
||||
|
||||
* `filename` {String}
|
||||
* `filename` {String | Buffer}
|
||||
* `listener` {Function}
|
||||
|
||||
Stop watching for changes on `filename`. If `listener` is specified, only that
|
||||
|
@ -1026,7 +1090,7 @@ when possible._
|
|||
|
||||
## fs.utimes(path, atime, mtime, callback)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `atime` {Integer}
|
||||
* `mtime` {Integer}
|
||||
* `callback` {Function}
|
||||
|
@ -1043,7 +1107,7 @@ follow the below rules:
|
|||
|
||||
## fs.utimesSync(path, atime, mtime)
|
||||
|
||||
* `path` {String}
|
||||
* `path` {String | Buffer}
|
||||
* `atime` {Integer}
|
||||
* `mtime` {Integer}
|
||||
|
||||
|
@ -1051,19 +1115,24 @@ Synchronous version of [`fs.utimes()`][]. Returns `undefined`.
|
|||
|
||||
## fs.watch(filename[, options][, listener])
|
||||
|
||||
* `filename` {String}
|
||||
* `options` {Object}
|
||||
* `filename` {String | Buffer}
|
||||
* `options` {String | Object}
|
||||
* `persistent` {Boolean} Indicates whether the process should continue to run
|
||||
as long as files are being watched. default = `true`
|
||||
* `recursive` {Boolean} Indicates whether all subdirectories should be
|
||||
watched, or only the current directory. The applies when a directory is
|
||||
specified, and only on supported platforms (See [Caveats][]). default =
|
||||
`false`
|
||||
* `encoding` {String} Specifies the character encoding to be used for the
|
||||
filename passed to the listener. default = `'utf8'`
|
||||
* `listener` {Function}
|
||||
|
||||
Watch for changes on `filename`, where `filename` is either a file or a
|
||||
directory. The returned object is a [`fs.FSWatcher`][].
|
||||
|
||||
The second argument is optional. If `options` is provided as a string, it
|
||||
specifies the `encoding`. Otherwise `options` should be passed as an object.
|
||||
|
||||
The listener callback gets two arguments `(event, filename)`. `event` is either
|
||||
`'rename'` or `'change'`, and `filename` is the name of the file which triggered
|
||||
the event.
|
||||
|
@ -1120,7 +1189,7 @@ fs.watch('somedir', (event, filename) => {
|
|||
|
||||
## fs.watchFile(filename[, options], listener)
|
||||
|
||||
* `filename` {String}
|
||||
* `filename` {String | Buffer}
|
||||
* `options` {Object}
|
||||
* `persistent` {Boolean}
|
||||
* `interval` {Integer}
|
||||
|
@ -1224,7 +1293,7 @@ the end of the file.
|
|||
|
||||
## fs.writeFile(file, data[, options], callback)
|
||||
|
||||
* `file` {String | Integer} filename or file descriptor
|
||||
* `file` {String | Buffer | Integer} filename or file descriptor
|
||||
* `data` {String | Buffer}
|
||||
* `options` {Object | String}
|
||||
* `encoding` {String | Null} default = `'utf8'`
|
||||
|
@ -1263,7 +1332,7 @@ _Note: Specified file descriptors will not be closed automatically._
|
|||
|
||||
## fs.writeFileSync(file, data[, options])
|
||||
|
||||
* `file` {String | Integer} filename or file descriptor
|
||||
* `file` {String | Buffer | Integer} filename or file descriptor
|
||||
* `data` {String | Buffer}
|
||||
* `options` {Object | String}
|
||||
* `encoding` {String | Null} default = `'utf8'`
|
||||
|
|
106
lib/fs.js
106
lib/fs.js
|
@ -903,17 +903,32 @@ fs.mkdirSync = function(path, mode) {
|
|||
modeNum(mode, 0o777));
|
||||
};
|
||||
|
||||
fs.readdir = function(path, callback) {
|
||||
fs.readdir = function(path, options, callback) {
|
||||
options = options || {};
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (typeof options === 'string') {
|
||||
options = {encoding: options};
|
||||
}
|
||||
if (typeof options !== 'object')
|
||||
throw new TypeError('"options" must be a string or an object');
|
||||
|
||||
callback = makeCallback(callback);
|
||||
if (!nullCheck(path, callback)) return;
|
||||
var req = new FSReqWrap();
|
||||
req.oncomplete = callback;
|
||||
binding.readdir(pathModule._makeLong(path), req);
|
||||
binding.readdir(pathModule._makeLong(path), options.encoding, req);
|
||||
};
|
||||
|
||||
fs.readdirSync = function(path) {
|
||||
fs.readdirSync = function(path, options) {
|
||||
options = options || {};
|
||||
if (typeof options === 'string')
|
||||
options = {encoding: options};
|
||||
if (typeof options !== 'object')
|
||||
throw new TypeError('"options" must be a string or an object');
|
||||
nullCheck(path);
|
||||
return binding.readdir(pathModule._makeLong(path));
|
||||
return binding.readdir(pathModule._makeLong(path), options.encoding);
|
||||
};
|
||||
|
||||
fs.fstat = function(fd, callback) {
|
||||
|
@ -952,17 +967,31 @@ fs.statSync = function(path) {
|
|||
return binding.stat(pathModule._makeLong(path));
|
||||
};
|
||||
|
||||
fs.readlink = function(path, callback) {
|
||||
fs.readlink = function(path, options, callback) {
|
||||
options = options || {};
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (typeof options === 'string') {
|
||||
options = {encoding: options};
|
||||
}
|
||||
if (typeof options !== 'object')
|
||||
throw new TypeError('"options" must be a string or an object');
|
||||
callback = makeCallback(callback);
|
||||
if (!nullCheck(path, callback)) return;
|
||||
var req = new FSReqWrap();
|
||||
req.oncomplete = callback;
|
||||
binding.readlink(pathModule._makeLong(path), req);
|
||||
binding.readlink(pathModule._makeLong(path), options.encoding, req);
|
||||
};
|
||||
|
||||
fs.readlinkSync = function(path) {
|
||||
fs.readlinkSync = function(path, options) {
|
||||
options = options || {};
|
||||
if (typeof options === 'string')
|
||||
options = {encoding: options};
|
||||
if (typeof options !== 'object')
|
||||
throw new TypeError('"options" must be a string or an object');
|
||||
nullCheck(path);
|
||||
return binding.readlink(pathModule._makeLong(path));
|
||||
return binding.readlink(pathModule._makeLong(path), options.encoding);
|
||||
};
|
||||
|
||||
function preprocessSymlinkDestination(path, type, linkPath) {
|
||||
|
@ -1363,11 +1392,15 @@ function FSWatcher() {
|
|||
}
|
||||
util.inherits(FSWatcher, EventEmitter);
|
||||
|
||||
FSWatcher.prototype.start = function(filename, persistent, recursive) {
|
||||
FSWatcher.prototype.start = function(filename,
|
||||
persistent,
|
||||
recursive,
|
||||
encoding) {
|
||||
nullCheck(filename);
|
||||
var err = this._handle.start(pathModule._makeLong(filename),
|
||||
persistent,
|
||||
recursive);
|
||||
recursive,
|
||||
encoding);
|
||||
if (err) {
|
||||
this._handle.close();
|
||||
const error = errnoException(err, `watch ${filename}`);
|
||||
|
@ -1380,25 +1413,27 @@ FSWatcher.prototype.close = function() {
|
|||
this._handle.close();
|
||||
};
|
||||
|
||||
fs.watch = function(filename) {
|
||||
fs.watch = function(filename, options, listener) {
|
||||
nullCheck(filename);
|
||||
var watcher;
|
||||
var options;
|
||||
var listener;
|
||||
|
||||
if (arguments[1] !== null && typeof arguments[1] === 'object') {
|
||||
options = arguments[1];
|
||||
listener = arguments[2];
|
||||
} else {
|
||||
options = options || {};
|
||||
if (typeof options === 'function') {
|
||||
listener = options;
|
||||
options = {};
|
||||
listener = arguments[1];
|
||||
} else if (typeof options === 'string') {
|
||||
options = {encoding: options};
|
||||
}
|
||||
if (typeof options !== 'object')
|
||||
throw new TypeError('"options" must be a string or an object');
|
||||
|
||||
if (options.persistent === undefined) options.persistent = true;
|
||||
if (options.recursive === undefined) options.recursive = false;
|
||||
|
||||
watcher = new FSWatcher();
|
||||
watcher.start(filename, options.persistent, options.recursive);
|
||||
const watcher = new FSWatcher();
|
||||
watcher.start(filename,
|
||||
options.persistent,
|
||||
options.recursive,
|
||||
options.encoding);
|
||||
|
||||
if (listener) {
|
||||
watcher.addListener('change', listener);
|
||||
|
@ -2139,10 +2174,19 @@ SyncWriteStream.prototype.destroy = function() {
|
|||
|
||||
SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy;
|
||||
|
||||
fs.mkdtemp = function(prefix, callback) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new TypeError('"callback" argument must be a function');
|
||||
fs.mkdtemp = function(prefix, options, callback) {
|
||||
if (!prefix || typeof prefix !== 'string')
|
||||
throw new TypeError('filename prefix is required');
|
||||
|
||||
options = options || {};
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (typeof options === 'string') {
|
||||
options = {encoding: options};
|
||||
}
|
||||
if (typeof options !== 'object')
|
||||
throw new TypeError('"options" must be a string or an object');
|
||||
|
||||
if (!nullCheck(prefix, callback)) {
|
||||
return;
|
||||
|
@ -2151,11 +2195,19 @@ fs.mkdtemp = function(prefix, callback) {
|
|||
var req = new FSReqWrap();
|
||||
req.oncomplete = callback;
|
||||
|
||||
binding.mkdtemp(prefix + 'XXXXXX', req);
|
||||
binding.mkdtemp(prefix + 'XXXXXX', options.encoding, req);
|
||||
};
|
||||
|
||||
fs.mkdtempSync = function(prefix) {
|
||||
fs.mkdtempSync = function(prefix, options) {
|
||||
if (!prefix || typeof prefix !== 'string')
|
||||
throw new TypeError('filename prefix is required');
|
||||
|
||||
options = options || {};
|
||||
if (typeof options === 'string')
|
||||
options = {encoding: options};
|
||||
if (typeof options !== 'object')
|
||||
throw new TypeError('"options" must be a string or an object');
|
||||
nullCheck(prefix);
|
||||
|
||||
return binding.mkdtemp(prefix + 'XXXXXX');
|
||||
return binding.mkdtemp(prefix + 'XXXXXX', options.encoding);
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "util-inl.h"
|
||||
#include "node.h"
|
||||
#include "handle_wrap.h"
|
||||
#include "string_bytes.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -41,6 +42,7 @@ class FSEventWrap: public HandleWrap {
|
|||
|
||||
uv_fs_event_t handle_;
|
||||
bool initialized_;
|
||||
enum encoding encoding_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -86,16 +88,20 @@ void FSEventWrap::Start(const FunctionCallbackInfo<Value>& args) {
|
|||
|
||||
FSEventWrap* wrap = Unwrap<FSEventWrap>(args.Holder());
|
||||
|
||||
if (args.Length() < 1 || !args[0]->IsString()) {
|
||||
return env->ThrowTypeError("filename must be a valid string");
|
||||
}
|
||||
static const char kErrMsg[] = "filename must be a string or Buffer";
|
||||
if (args.Length() < 1)
|
||||
return env->ThrowTypeError(kErrMsg);
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
if (*path == nullptr)
|
||||
return env->ThrowTypeError(kErrMsg);
|
||||
|
||||
unsigned int flags = 0;
|
||||
if (args[2]->IsTrue())
|
||||
flags |= UV_FS_EVENT_RECURSIVE;
|
||||
|
||||
wrap->encoding_ = ParseEncoding(env->isolate(), args[3], UTF8);
|
||||
|
||||
int err = uv_fs_event_init(wrap->env()->event_loop(), &wrap->handle_);
|
||||
if (err == 0) {
|
||||
wrap->initialized_ = true;
|
||||
|
@ -156,7 +162,18 @@ void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename,
|
|||
};
|
||||
|
||||
if (filename != nullptr) {
|
||||
argv[2] = OneByteString(env->isolate(), filename);
|
||||
Local<Value> fn = StringBytes::Encode(env->isolate(),
|
||||
filename,
|
||||
wrap->encoding_);
|
||||
if (fn.IsEmpty()) {
|
||||
argv[0] = Integer::New(env->isolate(), UV_EINVAL);
|
||||
argv[2] = StringBytes::Encode(env->isolate(),
|
||||
filename,
|
||||
strlen(filename),
|
||||
BUFFER);
|
||||
} else {
|
||||
argv[2] = fn;
|
||||
}
|
||||
}
|
||||
|
||||
wrap->MakeCallback(env->onchange_string(), ARRAY_SIZE(argv), argv);
|
||||
|
|
295
src/node_file.cc
295
src/node_file.cc
|
@ -34,6 +34,7 @@ using v8::FunctionCallbackInfo;
|
|||
using v8::FunctionTemplate;
|
||||
using v8::HandleScope;
|
||||
using v8::Integer;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::Number;
|
||||
using v8::Object;
|
||||
|
@ -56,6 +57,7 @@ class FSReqWrap: public ReqWrap<uv_fs_t> {
|
|||
Local<Object> req,
|
||||
const char* syscall,
|
||||
const char* data = nullptr,
|
||||
enum encoding encoding = UTF8,
|
||||
Ownership ownership = COPY);
|
||||
|
||||
inline void Dispose();
|
||||
|
@ -69,6 +71,7 @@ class FSReqWrap: public ReqWrap<uv_fs_t> {
|
|||
|
||||
const char* syscall() const { return syscall_; }
|
||||
const char* data() const { return data_; }
|
||||
const enum encoding encoding_;
|
||||
|
||||
size_t self_size() const override { return sizeof(*this); }
|
||||
|
||||
|
@ -76,8 +79,10 @@ class FSReqWrap: public ReqWrap<uv_fs_t> {
|
|||
FSReqWrap(Environment* env,
|
||||
Local<Object> req,
|
||||
const char* syscall,
|
||||
const char* data)
|
||||
const char* data,
|
||||
enum encoding encoding)
|
||||
: ReqWrap(env, req, AsyncWrap::PROVIDER_FSREQWRAP),
|
||||
encoding_(encoding),
|
||||
syscall_(syscall),
|
||||
data_(data) {
|
||||
Wrap(object(), this);
|
||||
|
@ -95,17 +100,21 @@ class FSReqWrap: public ReqWrap<uv_fs_t> {
|
|||
DISALLOW_COPY_AND_ASSIGN(FSReqWrap);
|
||||
};
|
||||
|
||||
#define ASSERT_PATH(path) \
|
||||
if (*path == nullptr) \
|
||||
return TYPE_ERROR( #path " must be a string or Buffer");
|
||||
|
||||
FSReqWrap* FSReqWrap::New(Environment* env,
|
||||
Local<Object> req,
|
||||
const char* syscall,
|
||||
const char* data,
|
||||
enum encoding encoding,
|
||||
Ownership ownership) {
|
||||
const bool copy = (data != nullptr && ownership == COPY);
|
||||
const size_t size = copy ? 1 + strlen(data) : 0;
|
||||
FSReqWrap* that;
|
||||
char* const storage = new char[sizeof(*that) + size];
|
||||
that = new(storage) FSReqWrap(env, req, syscall, data);
|
||||
that = new(storage) FSReqWrap(env, req, syscall, data, encoding);
|
||||
if (copy)
|
||||
that->data_ = static_cast<char*>(memcpy(that->inline_data(), data, size));
|
||||
return that;
|
||||
|
@ -127,7 +136,6 @@ static inline bool IsInt64(double x) {
|
|||
return x == static_cast<double>(static_cast<int64_t>(x));
|
||||
}
|
||||
|
||||
|
||||
static void After(uv_fs_t *req) {
|
||||
FSReqWrap* req_wrap = static_cast<FSReqWrap*>(req->data);
|
||||
CHECK_EQ(&req_wrap->req_, req);
|
||||
|
@ -143,6 +151,7 @@ static void After(uv_fs_t *req) {
|
|||
// Allocate space for two args. We may only use one depending on the case.
|
||||
// (Feel free to increase this if you need more)
|
||||
Local<Value> argv[2];
|
||||
Local<Value> link;
|
||||
|
||||
if (req->result < 0) {
|
||||
// An error happened.
|
||||
|
@ -201,13 +210,35 @@ static void After(uv_fs_t *req) {
|
|||
break;
|
||||
|
||||
case UV_FS_MKDTEMP:
|
||||
argv[1] = String::NewFromUtf8(env->isolate(),
|
||||
static_cast<const char*>(req->path));
|
||||
link = StringBytes::Encode(env->isolate(),
|
||||
static_cast<const char*>(req->path),
|
||||
req_wrap->encoding_);
|
||||
if (link.IsEmpty()) {
|
||||
argv[0] = UVException(env->isolate(),
|
||||
UV_EINVAL,
|
||||
req_wrap->syscall(),
|
||||
"Invalid character encoding for filename",
|
||||
req->path,
|
||||
req_wrap->data());
|
||||
} else {
|
||||
argv[1] = link;
|
||||
}
|
||||
break;
|
||||
|
||||
case UV_FS_READLINK:
|
||||
argv[1] = String::NewFromUtf8(env->isolate(),
|
||||
static_cast<const char*>(req->ptr));
|
||||
link = StringBytes::Encode(env->isolate(),
|
||||
static_cast<const char*>(req->ptr),
|
||||
req_wrap->encoding_);
|
||||
if (link.IsEmpty()) {
|
||||
argv[0] = UVException(env->isolate(),
|
||||
UV_EINVAL,
|
||||
req_wrap->syscall(),
|
||||
"Invalid character encoding for link",
|
||||
req->path,
|
||||
req_wrap->data());
|
||||
} else {
|
||||
argv[1] = link;
|
||||
}
|
||||
break;
|
||||
|
||||
case UV_FS_READ:
|
||||
|
@ -237,8 +268,19 @@ static void After(uv_fs_t *req) {
|
|||
break;
|
||||
}
|
||||
|
||||
name_argv[name_idx++] =
|
||||
String::NewFromUtf8(env->isolate(), ent.name);
|
||||
Local<Value> filename = StringBytes::Encode(env->isolate(),
|
||||
ent.name,
|
||||
req_wrap->encoding_);
|
||||
if (filename.IsEmpty()) {
|
||||
argv[0] = UVException(env->isolate(),
|
||||
UV_EINVAL,
|
||||
req_wrap->syscall(),
|
||||
"Invalid character encoding for filename",
|
||||
req->path,
|
||||
req_wrap->data());
|
||||
break;
|
||||
}
|
||||
name_argv[name_idx++] = filename;
|
||||
|
||||
if (name_idx >= ARRAY_SIZE(name_argv)) {
|
||||
fn->Call(env->context(), names, name_idx, name_argv)
|
||||
|
@ -277,10 +319,11 @@ struct fs_req_wrap {
|
|||
};
|
||||
|
||||
|
||||
#define ASYNC_DEST_CALL(func, req, dest, ...) \
|
||||
#define ASYNC_DEST_CALL(func, req, dest, encoding, ...) \
|
||||
Environment* env = Environment::GetCurrent(args); \
|
||||
CHECK(req->IsObject()); \
|
||||
FSReqWrap* req_wrap = FSReqWrap::New(env, req.As<Object>(), #func, dest); \
|
||||
FSReqWrap* req_wrap = FSReqWrap::New(env, req.As<Object>(), \
|
||||
#func, dest, encoding); \
|
||||
int err = uv_fs_ ## func(env->event_loop(), \
|
||||
&req_wrap->req_, \
|
||||
__VA_ARGS__, \
|
||||
|
@ -296,8 +339,8 @@ struct fs_req_wrap {
|
|||
args.GetReturnValue().Set(req_wrap->persistent()); \
|
||||
}
|
||||
|
||||
#define ASYNC_CALL(func, req, ...) \
|
||||
ASYNC_DEST_CALL(func, req, nullptr, __VA_ARGS__) \
|
||||
#define ASYNC_CALL(func, req, encoding, ...) \
|
||||
ASYNC_DEST_CALL(func, req, nullptr, encoding, __VA_ARGS__) \
|
||||
|
||||
#define SYNC_DEST_CALL(func, path, dest, ...) \
|
||||
fs_req_wrap req_wrap; \
|
||||
|
@ -317,23 +360,22 @@ struct fs_req_wrap {
|
|||
|
||||
#define SYNC_RESULT err
|
||||
|
||||
|
||||
static void Access(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args.GetIsolate());
|
||||
HandleScope scope(env->isolate());
|
||||
|
||||
if (args.Length() < 2)
|
||||
return TYPE_ERROR("path and mode are required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
if (!args[1]->IsInt32())
|
||||
return TYPE_ERROR("mode must be an integer");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
int mode = static_cast<int>(args[1]->Int32Value());
|
||||
|
||||
if (args[2]->IsObject()) {
|
||||
ASYNC_CALL(access, args[2], *path, mode);
|
||||
ASYNC_CALL(access, args[2], UTF8, *path, mode);
|
||||
} else {
|
||||
SYNC_CALL(access, *path, *path, mode);
|
||||
}
|
||||
|
@ -351,7 +393,7 @@ static void Close(const FunctionCallbackInfo<Value>& args) {
|
|||
int fd = args[0]->Int32Value();
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(close, args[1], fd)
|
||||
ASYNC_CALL(close, args[1], UTF8, fd)
|
||||
} else {
|
||||
SYNC_CALL(close, 0, fd)
|
||||
}
|
||||
|
@ -544,13 +586,12 @@ static void Stat(const FunctionCallbackInfo<Value>& args) {
|
|||
|
||||
if (args.Length() < 1)
|
||||
return TYPE_ERROR("path required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(stat, args[1], *path)
|
||||
ASYNC_CALL(stat, args[1], UTF8, *path)
|
||||
} else {
|
||||
SYNC_CALL(stat, *path, *path)
|
||||
args.GetReturnValue().Set(
|
||||
|
@ -563,13 +604,12 @@ static void LStat(const FunctionCallbackInfo<Value>& args) {
|
|||
|
||||
if (args.Length() < 1)
|
||||
return TYPE_ERROR("path required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(lstat, args[1], *path)
|
||||
ASYNC_CALL(lstat, args[1], UTF8, *path)
|
||||
} else {
|
||||
SYNC_CALL(lstat, *path, *path)
|
||||
args.GetReturnValue().Set(
|
||||
|
@ -588,7 +628,7 @@ static void FStat(const FunctionCallbackInfo<Value>& args) {
|
|||
int fd = args[0]->Int32Value();
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(fstat, args[1], fd)
|
||||
ASYNC_CALL(fstat, args[1], UTF8, fd)
|
||||
} else {
|
||||
SYNC_CALL(fstat, 0, fd)
|
||||
args.GetReturnValue().Set(
|
||||
|
@ -604,13 +644,12 @@ static void Symlink(const FunctionCallbackInfo<Value>& args) {
|
|||
return TYPE_ERROR("target path required");
|
||||
if (len < 2)
|
||||
return TYPE_ERROR("src path required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("target path must be a string");
|
||||
if (!args[1]->IsString())
|
||||
return TYPE_ERROR("src path must be a string");
|
||||
|
||||
node::Utf8Value target(env->isolate(), args[0]);
|
||||
node::Utf8Value path(env->isolate(), args[1]);
|
||||
BufferValue target(env->isolate(), args[0]);
|
||||
ASSERT_PATH(target)
|
||||
BufferValue path(env->isolate(), args[1]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if (args[2]->IsString()) {
|
||||
|
@ -625,7 +664,7 @@ static void Symlink(const FunctionCallbackInfo<Value>& args) {
|
|||
}
|
||||
|
||||
if (args[3]->IsObject()) {
|
||||
ASYNC_DEST_CALL(symlink, args[3], *path, *target, *path, flags)
|
||||
ASYNC_DEST_CALL(symlink, args[3], *path, UTF8, *target, *path, flags)
|
||||
} else {
|
||||
SYNC_DEST_CALL(symlink, *target, *path, *target, *path, flags)
|
||||
}
|
||||
|
@ -639,37 +678,51 @@ static void Link(const FunctionCallbackInfo<Value>& args) {
|
|||
return TYPE_ERROR("src path required");
|
||||
if (len < 2)
|
||||
return TYPE_ERROR("dest path required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("src path must be a string");
|
||||
if (!args[1]->IsString())
|
||||
return TYPE_ERROR("dest path must be a string");
|
||||
|
||||
node::Utf8Value orig_path(env->isolate(), args[0]);
|
||||
node::Utf8Value new_path(env->isolate(), args[1]);
|
||||
BufferValue src(env->isolate(), args[0]);
|
||||
ASSERT_PATH(src)
|
||||
|
||||
BufferValue dest(env->isolate(), args[1]);
|
||||
ASSERT_PATH(dest)
|
||||
|
||||
if (args[2]->IsObject()) {
|
||||
ASYNC_DEST_CALL(link, args[2], *new_path, *orig_path, *new_path)
|
||||
ASYNC_DEST_CALL(link, args[2], *dest, UTF8, *src, *dest)
|
||||
} else {
|
||||
SYNC_DEST_CALL(link, *orig_path, *new_path, *orig_path, *new_path)
|
||||
SYNC_DEST_CALL(link, *src, *dest, *src, *dest)
|
||||
}
|
||||
}
|
||||
|
||||
static void ReadLink(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
if (args.Length() < 1)
|
||||
const int argc = args.Length();
|
||||
|
||||
if (argc < 1)
|
||||
return TYPE_ERROR("path required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(readlink, args[1], *path)
|
||||
const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8);
|
||||
|
||||
Local<Value> callback = Null(env->isolate());
|
||||
if (argc == 3)
|
||||
callback = args[2];
|
||||
|
||||
if (callback->IsObject()) {
|
||||
ASYNC_CALL(readlink, callback, encoding, *path)
|
||||
} else {
|
||||
SYNC_CALL(readlink, *path, *path)
|
||||
const char* link_path = static_cast<const char*>(SYNC_REQ.ptr);
|
||||
Local<String> rc = String::NewFromUtf8(env->isolate(), link_path);
|
||||
Local<Value> rc = StringBytes::Encode(env->isolate(),
|
||||
link_path,
|
||||
encoding);
|
||||
if (rc.IsEmpty()) {
|
||||
return env->ThrowUVException(UV_EINVAL,
|
||||
"readlink",
|
||||
"Invalid character encoding for link",
|
||||
*path);
|
||||
}
|
||||
args.GetReturnValue().Set(rc);
|
||||
}
|
||||
}
|
||||
|
@ -682,16 +735,14 @@ static void Rename(const FunctionCallbackInfo<Value>& args) {
|
|||
return TYPE_ERROR("old path required");
|
||||
if (len < 2)
|
||||
return TYPE_ERROR("new path required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("old path must be a string");
|
||||
if (!args[1]->IsString())
|
||||
return TYPE_ERROR("new path must be a string");
|
||||
|
||||
node::Utf8Value old_path(env->isolate(), args[0]);
|
||||
node::Utf8Value new_path(env->isolate(), args[1]);
|
||||
BufferValue old_path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(old_path)
|
||||
BufferValue new_path(env->isolate(), args[1]);
|
||||
ASSERT_PATH(new_path)
|
||||
|
||||
if (args[2]->IsObject()) {
|
||||
ASYNC_DEST_CALL(rename, args[2], *new_path, *old_path, *new_path)
|
||||
ASYNC_DEST_CALL(rename, args[2], *new_path, UTF8, *old_path, *new_path)
|
||||
} else {
|
||||
SYNC_DEST_CALL(rename, *old_path, *new_path, *old_path, *new_path)
|
||||
}
|
||||
|
@ -720,7 +771,7 @@ static void FTruncate(const FunctionCallbackInfo<Value>& args) {
|
|||
const int64_t len = len_v->IntegerValue();
|
||||
|
||||
if (args[2]->IsObject()) {
|
||||
ASYNC_CALL(ftruncate, args[2], fd, len)
|
||||
ASYNC_CALL(ftruncate, args[2], UTF8, fd, len)
|
||||
} else {
|
||||
SYNC_CALL(ftruncate, 0, fd, len)
|
||||
}
|
||||
|
@ -737,7 +788,7 @@ static void Fdatasync(const FunctionCallbackInfo<Value>& args) {
|
|||
int fd = args[0]->Int32Value();
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(fdatasync, args[1], fd)
|
||||
ASYNC_CALL(fdatasync, args[1], UTF8, fd)
|
||||
} else {
|
||||
SYNC_CALL(fdatasync, 0, fd)
|
||||
}
|
||||
|
@ -754,7 +805,7 @@ static void Fsync(const FunctionCallbackInfo<Value>& args) {
|
|||
int fd = args[0]->Int32Value();
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(fsync, args[1], fd)
|
||||
ASYNC_CALL(fsync, args[1], UTF8, fd)
|
||||
} else {
|
||||
SYNC_CALL(fsync, 0, fd)
|
||||
}
|
||||
|
@ -765,13 +816,12 @@ static void Unlink(const FunctionCallbackInfo<Value>& args) {
|
|||
|
||||
if (args.Length() < 1)
|
||||
return TYPE_ERROR("path required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(unlink, args[1], *path)
|
||||
ASYNC_CALL(unlink, args[1], UTF8, *path)
|
||||
} else {
|
||||
SYNC_CALL(unlink, *path, *path)
|
||||
}
|
||||
|
@ -782,13 +832,12 @@ static void RMDir(const FunctionCallbackInfo<Value>& args) {
|
|||
|
||||
if (args.Length() < 1)
|
||||
return TYPE_ERROR("path required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(rmdir, args[1], *path)
|
||||
ASYNC_CALL(rmdir, args[1], UTF8, *path)
|
||||
} else {
|
||||
SYNC_CALL(rmdir, *path, *path)
|
||||
}
|
||||
|
@ -799,16 +848,16 @@ static void MKDir(const FunctionCallbackInfo<Value>& args) {
|
|||
|
||||
if (args.Length() < 2)
|
||||
return TYPE_ERROR("path and mode are required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
if (!args[1]->IsInt32())
|
||||
return TYPE_ERROR("mode must be an integer");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
int mode = static_cast<int>(args[1]->Int32Value());
|
||||
|
||||
if (args[2]->IsObject()) {
|
||||
ASYNC_CALL(mkdir, args[2], *path, mode)
|
||||
ASYNC_CALL(mkdir, args[2], UTF8, *path, mode)
|
||||
} else {
|
||||
SYNC_CALL(mkdir, *path, *path, mode)
|
||||
}
|
||||
|
@ -817,15 +866,22 @@ static void MKDir(const FunctionCallbackInfo<Value>& args) {
|
|||
static void ReadDir(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
if (args.Length() < 1)
|
||||
const int argc = args.Length();
|
||||
|
||||
if (argc < 1)
|
||||
return TYPE_ERROR("path required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(scandir, args[1], *path, 0 /*flags*/)
|
||||
const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8);
|
||||
|
||||
Local<Value> callback = Null(env->isolate());
|
||||
if (argc == 3)
|
||||
callback = args[2];
|
||||
|
||||
if (callback->IsObject()) {
|
||||
ASYNC_CALL(scandir, callback, encoding, *path, 0 /*flags*/)
|
||||
} else {
|
||||
SYNC_CALL(scandir, *path, *path, 0 /*flags*/)
|
||||
|
||||
|
@ -845,8 +901,17 @@ static void ReadDir(const FunctionCallbackInfo<Value>& args) {
|
|||
if (r != 0)
|
||||
return env->ThrowUVException(r, "readdir", "", *path);
|
||||
|
||||
Local<Value> filename = StringBytes::Encode(env->isolate(),
|
||||
ent.name,
|
||||
encoding);
|
||||
if (filename.IsEmpty()) {
|
||||
return env->ThrowUVException(UV_EINVAL,
|
||||
"readdir",
|
||||
"Invalid character encoding for filename",
|
||||
*path);
|
||||
}
|
||||
|
||||
name_v[name_idx++] = String::NewFromUtf8(env->isolate(), ent.name);
|
||||
name_v[name_idx++] = filename;
|
||||
|
||||
if (name_idx >= ARRAY_SIZE(name_v)) {
|
||||
fn->Call(env->context(), names, name_idx, name_v)
|
||||
|
@ -873,19 +938,19 @@ static void Open(const FunctionCallbackInfo<Value>& args) {
|
|||
return TYPE_ERROR("flags required");
|
||||
if (len < 3)
|
||||
return TYPE_ERROR("mode required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
if (!args[1]->IsInt32())
|
||||
return TYPE_ERROR("flags must be an int");
|
||||
if (!args[2]->IsInt32())
|
||||
return TYPE_ERROR("mode must be an int");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
int flags = args[1]->Int32Value();
|
||||
int mode = static_cast<int>(args[2]->Int32Value());
|
||||
|
||||
if (args[3]->IsObject()) {
|
||||
ASYNC_CALL(open, args[3], *path, flags, mode)
|
||||
ASYNC_CALL(open, args[3], UTF8, *path, flags, mode)
|
||||
} else {
|
||||
SYNC_CALL(open, *path, *path, flags, mode)
|
||||
args.GetReturnValue().Set(SYNC_RESULT);
|
||||
|
@ -933,7 +998,7 @@ static void WriteBuffer(const FunctionCallbackInfo<Value>& args) {
|
|||
uv_buf_t uvbuf = uv_buf_init(const_cast<char*>(buf), len);
|
||||
|
||||
if (req->IsObject()) {
|
||||
ASYNC_CALL(write, req, fd, &uvbuf, 1, pos)
|
||||
ASYNC_CALL(write, req, UTF8, fd, &uvbuf, 1, pos)
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -983,7 +1048,7 @@ static void WriteBuffers(const FunctionCallbackInfo<Value>& args) {
|
|||
}
|
||||
|
||||
if (req->IsObject()) {
|
||||
ASYNC_CALL(write, req, fd, iovs, chunkCount, pos)
|
||||
ASYNC_CALL(write, req, UTF8, fd, iovs, chunkCount, pos)
|
||||
if (iovs != s_iovs)
|
||||
delete[] iovs;
|
||||
return;
|
||||
|
@ -1049,7 +1114,7 @@ static void WriteString(const FunctionCallbackInfo<Value>& args) {
|
|||
}
|
||||
|
||||
FSReqWrap* req_wrap =
|
||||
FSReqWrap::New(env, req.As<Object>(), "write", buf, ownership);
|
||||
FSReqWrap::New(env, req.As<Object>(), "write", buf, UTF8, ownership);
|
||||
int err = uv_fs_write(env->event_loop(),
|
||||
&req_wrap->req_,
|
||||
fd,
|
||||
|
@ -1123,7 +1188,7 @@ static void Read(const FunctionCallbackInfo<Value>& args) {
|
|||
req = args[5];
|
||||
|
||||
if (req->IsObject()) {
|
||||
ASYNC_CALL(read, req, fd, &uvbuf, 1, pos);
|
||||
ASYNC_CALL(read, req, UTF8, fd, &uvbuf, 1, pos);
|
||||
} else {
|
||||
SYNC_CALL(read, 0, fd, &uvbuf, 1, pos)
|
||||
args.GetReturnValue().Set(SYNC_RESULT);
|
||||
|
@ -1139,16 +1204,16 @@ static void Chmod(const FunctionCallbackInfo<Value>& args) {
|
|||
|
||||
if (args.Length() < 2)
|
||||
return TYPE_ERROR("path and mode are required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
if (!args[1]->IsInt32())
|
||||
return TYPE_ERROR("mode must be an integer");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
int mode = static_cast<int>(args[1]->Int32Value());
|
||||
|
||||
if (args[2]->IsObject()) {
|
||||
ASYNC_CALL(chmod, args[2], *path, mode);
|
||||
ASYNC_CALL(chmod, args[2], UTF8, *path, mode);
|
||||
} else {
|
||||
SYNC_CALL(chmod, *path, *path, mode);
|
||||
}
|
||||
|
@ -1172,7 +1237,7 @@ static void FChmod(const FunctionCallbackInfo<Value>& args) {
|
|||
int mode = static_cast<int>(args[1]->Int32Value());
|
||||
|
||||
if (args[2]->IsObject()) {
|
||||
ASYNC_CALL(fchmod, args[2], fd, mode);
|
||||
ASYNC_CALL(fchmod, args[2], UTF8, fd, mode);
|
||||
} else {
|
||||
SYNC_CALL(fchmod, 0, fd, mode);
|
||||
}
|
||||
|
@ -1192,19 +1257,19 @@ static void Chown(const FunctionCallbackInfo<Value>& args) {
|
|||
return TYPE_ERROR("uid required");
|
||||
if (len < 3)
|
||||
return TYPE_ERROR("gid required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
if (!args[1]->IsUint32())
|
||||
return TYPE_ERROR("uid must be an unsigned int");
|
||||
if (!args[2]->IsUint32())
|
||||
return TYPE_ERROR("gid must be an unsigned int");
|
||||
|
||||
node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
uv_uid_t uid = static_cast<uv_uid_t>(args[1]->Uint32Value());
|
||||
uv_gid_t gid = static_cast<uv_gid_t>(args[2]->Uint32Value());
|
||||
|
||||
if (args[3]->IsObject()) {
|
||||
ASYNC_CALL(chown, args[3], *path, uid, gid);
|
||||
ASYNC_CALL(chown, args[3], UTF8, *path, uid, gid);
|
||||
} else {
|
||||
SYNC_CALL(chown, *path, *path, uid, gid);
|
||||
}
|
||||
|
@ -1236,7 +1301,7 @@ static void FChown(const FunctionCallbackInfo<Value>& args) {
|
|||
uv_gid_t gid = static_cast<uv_gid_t>(args[2]->Uint32Value());
|
||||
|
||||
if (args[3]->IsObject()) {
|
||||
ASYNC_CALL(fchown, args[3], fd, uid, gid);
|
||||
ASYNC_CALL(fchown, args[3], UTF8, fd, uid, gid);
|
||||
} else {
|
||||
SYNC_CALL(fchown, 0, fd, uid, gid);
|
||||
}
|
||||
|
@ -1253,19 +1318,19 @@ static void UTimes(const FunctionCallbackInfo<Value>& args) {
|
|||
return TYPE_ERROR("atime required");
|
||||
if (len < 3)
|
||||
return TYPE_ERROR("mtime required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("path must be a string");
|
||||
if (!args[1]->IsNumber())
|
||||
return TYPE_ERROR("atime must be a number");
|
||||
if (!args[2]->IsNumber())
|
||||
return TYPE_ERROR("mtime must be a number");
|
||||
|
||||
const node::Utf8Value path(env->isolate(), args[0]);
|
||||
BufferValue path(env->isolate(), args[0]);
|
||||
ASSERT_PATH(path)
|
||||
|
||||
const double atime = static_cast<double>(args[1]->NumberValue());
|
||||
const double mtime = static_cast<double>(args[2]->NumberValue());
|
||||
|
||||
if (args[3]->IsObject()) {
|
||||
ASYNC_CALL(utime, args[3], *path, atime, mtime);
|
||||
ASYNC_CALL(utime, args[3], UTF8, *path, atime, mtime);
|
||||
} else {
|
||||
SYNC_CALL(utime, *path, *path, atime, mtime);
|
||||
}
|
||||
|
@ -1293,7 +1358,7 @@ static void FUTimes(const FunctionCallbackInfo<Value>& args) {
|
|||
const double mtime = static_cast<double>(args[2]->NumberValue());
|
||||
|
||||
if (args[3]->IsObject()) {
|
||||
ASYNC_CALL(futime, args[3], fd, atime, mtime);
|
||||
ASYNC_CALL(futime, args[3], UTF8, fd, atime, mtime);
|
||||
} else {
|
||||
SYNC_CALL(futime, 0, fd, atime, mtime);
|
||||
}
|
||||
|
@ -1302,19 +1367,27 @@ static void FUTimes(const FunctionCallbackInfo<Value>& args) {
|
|||
static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
if (args.Length() < 1)
|
||||
return TYPE_ERROR("template is required");
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("template must be a string");
|
||||
CHECK_GE(args.Length(), 2);
|
||||
|
||||
node::Utf8Value tmpl(env->isolate(), args[0]);
|
||||
BufferValue tmpl(env->isolate(), args[0]);
|
||||
if (*tmpl == nullptr)
|
||||
return TYPE_ERROR("template must be a string or Buffer");
|
||||
|
||||
if (args[1]->IsObject()) {
|
||||
ASYNC_CALL(mkdtemp, args[1], *tmpl);
|
||||
const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8);
|
||||
|
||||
if (args[2]->IsObject()) {
|
||||
ASYNC_CALL(mkdtemp, args[2], encoding, *tmpl);
|
||||
} else {
|
||||
SYNC_CALL(mkdtemp, *tmpl, *tmpl);
|
||||
args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(),
|
||||
SYNC_REQ.path));
|
||||
const char* path = static_cast<const char*>(SYNC_REQ.path);
|
||||
Local<Value> rc = StringBytes::Encode(env->isolate(), path, encoding);
|
||||
if (rc.IsEmpty()) {
|
||||
return env->ThrowUVException(UV_EINVAL,
|
||||
"mkdtemp",
|
||||
"Invalid character encoding for filename",
|
||||
*tmpl);
|
||||
}
|
||||
args.GetReturnValue().Set(rc);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ using v8::String;
|
|||
using v8::Value;
|
||||
using v8::MaybeLocal;
|
||||
|
||||
|
||||
template <typename ResourceType, typename TypeName>
|
||||
class ExternString: public ResourceType {
|
||||
public:
|
||||
|
@ -895,4 +894,34 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
|
|||
return val;
|
||||
}
|
||||
|
||||
Local<Value> StringBytes::Encode(Isolate* isolate,
|
||||
const char* buf,
|
||||
enum encoding encoding) {
|
||||
const size_t len = strlen(buf);
|
||||
Local<Value> ret;
|
||||
if (encoding == UCS2) {
|
||||
// In Node, UCS2 means utf16le. The data must be in little-endian
|
||||
// order and must be aligned on 2-bytes. This returns an empty
|
||||
// value if it's not aligned and ensures the appropriate byte order
|
||||
// on big endian architectures.
|
||||
const bool be = IsBigEndian();
|
||||
if (len % 2 != 0)
|
||||
return ret;
|
||||
std::vector<uint16_t> vec(len / 2);
|
||||
for (size_t i = 0, k = 0; i < len; i += 2, k += 1) {
|
||||
const uint8_t hi = static_cast<uint8_t>(buf[i + 0]);
|
||||
const uint8_t lo = static_cast<uint8_t>(buf[i + 1]);
|
||||
vec[k] = be ?
|
||||
static_cast<uint16_t>(hi) << 8 | lo
|
||||
: static_cast<uint16_t>(lo) << 8 | hi;
|
||||
}
|
||||
ret = vec.empty() ?
|
||||
static_cast< Local<Value> >(String::Empty(isolate))
|
||||
: StringBytes::Encode(isolate, &vec[0], vec.size());
|
||||
} else {
|
||||
ret = StringBytes::Encode(isolate, buf, len, encoding);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
|
|
@ -106,6 +106,10 @@ class StringBytes {
|
|||
const uint16_t* buf,
|
||||
size_t buflen);
|
||||
|
||||
static v8::Local<v8::Value> Encode(v8::Isolate* isolate,
|
||||
const char* buf,
|
||||
enum encoding encoding);
|
||||
|
||||
// Deprecated legacy interface
|
||||
|
||||
NODE_DEPRECATED("Use IsValidString(isolate, ...)",
|
||||
|
|
73
src/util.cc
73
src/util.cc
|
@ -1,37 +1,48 @@
|
|||
#include "util.h"
|
||||
#include "string_bytes.h"
|
||||
#include "node_buffer.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace node {
|
||||
|
||||
Utf8Value::Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value)
|
||||
using v8::Isolate;
|
||||
using v8::String;
|
||||
using v8::Local;
|
||||
using v8::Value;
|
||||
|
||||
static int MakeUtf8String(Isolate* isolate,
|
||||
Local<Value> value,
|
||||
char** dst,
|
||||
const size_t size) {
|
||||
Local<String> string = value->ToString(isolate);
|
||||
if (string.IsEmpty())
|
||||
return 0;
|
||||
size_t len = StringBytes::StorageSize(isolate, string, UTF8) + 1;
|
||||
if (len > size) {
|
||||
*dst = static_cast<char*>(malloc(len));
|
||||
CHECK_NE(*dst, nullptr);
|
||||
}
|
||||
const int flags =
|
||||
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8;
|
||||
const int length = string->WriteUtf8(*dst, len, 0, flags);
|
||||
(*dst)[length] = '\0';
|
||||
return length;
|
||||
}
|
||||
|
||||
Utf8Value::Utf8Value(Isolate* isolate, Local<Value> value)
|
||||
: length_(0), str_(str_st_) {
|
||||
if (value.IsEmpty())
|
||||
return;
|
||||
|
||||
v8::Local<v8::String> string = value->ToString(isolate);
|
||||
if (string.IsEmpty())
|
||||
return;
|
||||
|
||||
// Allocate enough space to include the null terminator
|
||||
size_t len = StringBytes::StorageSize(isolate, string, UTF8) + 1;
|
||||
if (len > sizeof(str_st_)) {
|
||||
str_ = static_cast<char*>(malloc(len));
|
||||
CHECK_NE(str_, nullptr);
|
||||
}
|
||||
|
||||
const int flags =
|
||||
v8::String::NO_NULL_TERMINATION | v8::String::REPLACE_INVALID_UTF8;
|
||||
length_ = string->WriteUtf8(str_, len, 0, flags);
|
||||
str_[length_] = '\0';
|
||||
length_ = MakeUtf8String(isolate, value, &str_, sizeof(str_st_));
|
||||
}
|
||||
|
||||
|
||||
TwoByteValue::TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value)
|
||||
TwoByteValue::TwoByteValue(Isolate* isolate, Local<Value> value)
|
||||
: length_(0), str_(str_st_) {
|
||||
if (value.IsEmpty())
|
||||
return;
|
||||
|
||||
v8::Local<v8::String> string = value->ToString(isolate);
|
||||
Local<String> string = value->ToString(isolate);
|
||||
if (string.IsEmpty())
|
||||
return;
|
||||
|
||||
|
@ -43,9 +54,31 @@ TwoByteValue::TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value)
|
|||
}
|
||||
|
||||
const int flags =
|
||||
v8::String::NO_NULL_TERMINATION | v8::String::REPLACE_INVALID_UTF8;
|
||||
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8;
|
||||
length_ = string->Write(str_, 0, len, flags);
|
||||
str_[length_] = '\0';
|
||||
}
|
||||
|
||||
BufferValue::BufferValue(Isolate* isolate, Local<Value> value)
|
||||
: str_(str_st_), fail_(true) {
|
||||
// Slightly different take on Utf8Value. If value is a String,
|
||||
// it will return a Utf8 encoded string. If value is a Buffer,
|
||||
// it will copy the data out of the Buffer as is.
|
||||
if (value.IsEmpty())
|
||||
return;
|
||||
if (value->IsString()) {
|
||||
MakeUtf8String(isolate, value, &str_, sizeof(str_st_));
|
||||
fail_ = false;
|
||||
} else if (Buffer::HasInstance(value)) {
|
||||
size_t len = Buffer::Length(value) + 1;
|
||||
if (len > sizeof(str_st_)) {
|
||||
str_ = static_cast<char*>(malloc(len));
|
||||
CHECK_NE(str_, nullptr);
|
||||
}
|
||||
memcpy(str_, Buffer::Data(value), len);
|
||||
str_[len - 1] = '\0';
|
||||
fail_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
|
19
src/util.h
19
src/util.h
|
@ -232,6 +232,25 @@ class TwoByteValue {
|
|||
uint16_t str_st_[1024];
|
||||
};
|
||||
|
||||
class BufferValue {
|
||||
public:
|
||||
explicit BufferValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
||||
|
||||
~BufferValue() {
|
||||
if (str_ != str_st_)
|
||||
free(str_);
|
||||
}
|
||||
|
||||
const char* operator*() const {
|
||||
return fail_ ? nullptr : str_;
|
||||
};
|
||||
|
||||
private:
|
||||
char* str_;
|
||||
char str_st_[1024];
|
||||
bool fail_;
|
||||
};
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // SRC_UTIL_H_
|
||||
|
|
|
@ -57,8 +57,14 @@ function rmdirSync(p, originalEr) {
|
|||
if (e.code === 'ENOTDIR')
|
||||
throw originalEr;
|
||||
if (e.code === 'ENOTEMPTY' || e.code === 'EEXIST' || e.code === 'EPERM') {
|
||||
fs.readdirSync(p).forEach(function(f) {
|
||||
rimrafSync(path.join(p, f));
|
||||
const enc = process.platform === 'linux' ? 'buffer' : 'utf8';
|
||||
fs.readdirSync(p, enc).forEach((f) => {
|
||||
if (f instanceof Buffer) {
|
||||
const buf = Buffer.concat([Buffer.from(p), Buffer.from(path.sep), f]);
|
||||
rimrafSync(buf);
|
||||
} else {
|
||||
rimrafSync(path.join(p, f));
|
||||
}
|
||||
});
|
||||
fs.rmdirSync(p);
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ fs.access(readOnlyFile, fs.W_OK, function(err) {
|
|||
|
||||
assert.throws(function() {
|
||||
fs.access(100, fs.F_OK, function(err) {});
|
||||
}, /path must be a string/);
|
||||
}, /path must be a string or Buffer/);
|
||||
|
||||
assert.throws(function() {
|
||||
fs.access(__filename, fs.F_OK);
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
common.refreshTmpDir();
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
fs.access(Buffer.from(common.tmpDir), common.mustCall((err) => {
|
||||
if (err) throw err;
|
||||
}));
|
||||
});
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
const buf = Buffer.from(path.join(common.tmpDir, 'a.txt'));
|
||||
fs.open(buf, 'w+', common.mustCall((err, fd) => {
|
||||
if (err) throw err;
|
||||
assert(fd);
|
||||
fs.close(fd, common.mustCall(() => {
|
||||
fs.unlinkSync(buf);
|
||||
}));
|
||||
}));
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.accessSync(true);
|
||||
}, /path must be a string or Buffer/);
|
||||
|
||||
const dir = Buffer.from(common.fixturesDir);
|
||||
fs.readdir(dir, 'hex', common.mustCall((err, list) => {
|
||||
if (err) throw err;
|
||||
list = list.map((i) => {
|
||||
return Buffer.from(i, 'hex').toString();
|
||||
});
|
||||
fs.readdir(dir, common.mustCall((err, list2) => {
|
||||
if (err) throw err;
|
||||
assert.deepStrictEqual(list, list2);
|
||||
}));
|
||||
}));
|
|
@ -25,12 +25,12 @@ assert.throws(
|
|||
function() {
|
||||
fs.link();
|
||||
},
|
||||
/src path/
|
||||
/src must be a string or Buffer/
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
function() {
|
||||
fs.link('abc');
|
||||
},
|
||||
/dest path/
|
||||
/dest must be a string or Buffer/
|
||||
);
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
|
||||
if (process.platform !== 'linux') {
|
||||
console.log('1..0 # Skipped: Test is linux specific.');
|
||||
return;
|
||||
}
|
||||
|
||||
common.refreshTmpDir();
|
||||
const filename = '\uD83D\uDC04';
|
||||
const root = Buffer.from(`${common.tmpDir}${path.sep}`);
|
||||
const filebuff = Buffer.from(filename, 'ucs2');
|
||||
const fullpath = Buffer.concat([root, filebuff]);
|
||||
|
||||
fs.closeSync(fs.openSync(fullpath, 'w+'));
|
||||
|
||||
fs.readdir(common.tmpDir, 'ucs2', (err, list) => {
|
||||
if (err) throw err;
|
||||
assert.equal(1, list.length);
|
||||
const fn = list[0];
|
||||
assert.deepStrictEqual(filebuff, Buffer.from(fn, 'ucs2'));
|
||||
assert.strictEqual(fn, filename);
|
||||
});
|
||||
|
||||
process.on('exit', () => {
|
||||
fs.unlinkSync(fullpath);
|
||||
});
|
|
@ -0,0 +1,52 @@
|
|||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const assert = require('assert');
|
||||
|
||||
if (common.isFreeBSD) {
|
||||
console.log('1..0 # Skipped: Test currently not working on FreeBSD');
|
||||
return;
|
||||
}
|
||||
|
||||
const fn = '新建文夹件.txt';
|
||||
const a = path.join(common.tmpDir, fn);
|
||||
|
||||
const watcher1 = fs.watch(
|
||||
common.tmpDir,
|
||||
{encoding: 'hex'},
|
||||
(event, filename) => {
|
||||
if (filename)
|
||||
assert.equal(filename, 'e696b0e5bbbae69687e5a4b9e4bbb62e747874');
|
||||
watcher1.close();
|
||||
}
|
||||
);
|
||||
|
||||
const watcher2 = fs.watch(
|
||||
common.tmpDir,
|
||||
(event, filename) => {
|
||||
if (filename)
|
||||
assert.equal(filename, fn);
|
||||
watcher2.close();
|
||||
}
|
||||
);
|
||||
|
||||
const watcher3 = fs.watch(
|
||||
common.tmpDir,
|
||||
{encoding: 'buffer'},
|
||||
(event, filename) => {
|
||||
if (filename) {
|
||||
assert(filename instanceof Buffer);
|
||||
assert.equal(filename.toString('utf8'), fn);
|
||||
}
|
||||
watcher3.close();
|
||||
}
|
||||
);
|
||||
|
||||
const fd = fs.openSync(a, 'w+');
|
||||
fs.closeSync(fd);
|
||||
|
||||
process.on('exit', () => {
|
||||
fs.unlink(a);
|
||||
});
|
|
@ -247,7 +247,7 @@ assert.deepEqual(children, {
|
|||
assert.throws(function() {
|
||||
console.error('require non-string');
|
||||
require({ foo: 'bar' });
|
||||
}, 'path must be a string');
|
||||
}, 'path must be a string or Buffer');
|
||||
|
||||
assert.throws(function() {
|
||||
console.error('require empty string');
|
||||
|
|
Загрузка…
Ссылка в новой задаче