mirror of
https://github.com/nodejs/node.git
synced 2024-12-01 16:10:02 +01:00
0bba590283
All compile time warnings about using deprecated APIs have been suppressed by updating node's API. Though there are still many function calls that can accept Isolate, and still need to be updated. node_isolate had to be added as an extern variable in node.h and node_object_wrap.h Also a couple small fixes for Error handling. Before v8 3.16.6 the error stack message was lazily written when it was needed, which allowed you to change the message after instantiation. Then the stack would be written with the new message the first time it was accessed. Though that has changed. Now it creates the stack message on instantiation. So setting a different message afterwards won't be displayed. This is not a complete fix for the problem. Getting error without any message isn't very useful.
1747 lines
44 KiB
JavaScript
1747 lines
44 KiB
JavaScript
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
// Maintainers, keep in mind that octal literals are not allowed
|
|
// in strict mode. Use the decimal value and add a comment with
|
|
// the octal value. Example:
|
|
//
|
|
// var mode = 438; /* mode=0666 */
|
|
|
|
var util = require('util');
|
|
var pathModule = require('path');
|
|
|
|
var binding = process.binding('fs');
|
|
var constants = process.binding('constants');
|
|
var fs = exports;
|
|
var Stream = require('stream').Stream;
|
|
var EventEmitter = require('events').EventEmitter;
|
|
|
|
var Readable = Stream.Readable;
|
|
var Writable = Stream.Writable;
|
|
|
|
var kMinPoolSpace = 128;
|
|
|
|
var O_APPEND = constants.O_APPEND || 0;
|
|
var O_CREAT = constants.O_CREAT || 0;
|
|
var O_DIRECTORY = constants.O_DIRECTORY || 0;
|
|
var O_EXCL = constants.O_EXCL || 0;
|
|
var O_NOCTTY = constants.O_NOCTTY || 0;
|
|
var O_NOFOLLOW = constants.O_NOFOLLOW || 0;
|
|
var O_RDONLY = constants.O_RDONLY || 0;
|
|
var O_RDWR = constants.O_RDWR || 0;
|
|
var O_SYMLINK = constants.O_SYMLINK || 0;
|
|
var O_SYNC = constants.O_SYNC || 0;
|
|
var O_TRUNC = constants.O_TRUNC || 0;
|
|
var O_WRONLY = constants.O_WRONLY || 0;
|
|
|
|
var isWindows = process.platform === 'win32';
|
|
|
|
var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG);
|
|
|
|
function rethrow() {
|
|
// Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and
|
|
// is fairly slow to generate.
|
|
if (DEBUG) {
|
|
var backtrace = new Error;
|
|
return function(err) {
|
|
if (err) {
|
|
backtrace.stack = err.name + ': ' + err.message +
|
|
backtrace.stack.substr(backtrace.name.length);
|
|
err = backtrace;
|
|
throw err;
|
|
}
|
|
};
|
|
}
|
|
|
|
return function(err) {
|
|
if (err) {
|
|
throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs
|
|
}
|
|
};
|
|
}
|
|
|
|
function maybeCallback(cb) {
|
|
return typeof cb === 'function' ? cb : rethrow();
|
|
}
|
|
|
|
// Ensure that callbacks run in the global context. Only use this function
|
|
// for callbacks that are passed to the binding layer, callbacks that are
|
|
// invoked from JS already run in the proper scope.
|
|
function makeCallback(cb) {
|
|
if (typeof cb !== 'function') {
|
|
return rethrow();
|
|
}
|
|
|
|
return function() {
|
|
return cb.apply(null, arguments);
|
|
};
|
|
}
|
|
|
|
function assertEncoding(encoding) {
|
|
if (encoding && !Buffer.isEncoding(encoding)) {
|
|
throw new Error('Unknown encoding: ' + encoding);
|
|
}
|
|
}
|
|
|
|
function nullCheck(path, callback) {
|
|
if (('' + path).indexOf('\u0000') !== -1) {
|
|
var er = new Error('Path must be a string without null bytes.');
|
|
if (!callback)
|
|
throw er;
|
|
process.nextTick(function() {
|
|
callback(er);
|
|
});
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
fs.Stats = binding.Stats;
|
|
|
|
fs.Stats.prototype._checkModeProperty = function(property) {
|
|
return ((this.mode & constants.S_IFMT) === property);
|
|
};
|
|
|
|
fs.Stats.prototype.isDirectory = function() {
|
|
return this._checkModeProperty(constants.S_IFDIR);
|
|
};
|
|
|
|
fs.Stats.prototype.isFile = function() {
|
|
return this._checkModeProperty(constants.S_IFREG);
|
|
};
|
|
|
|
fs.Stats.prototype.isBlockDevice = function() {
|
|
return this._checkModeProperty(constants.S_IFBLK);
|
|
};
|
|
|
|
fs.Stats.prototype.isCharacterDevice = function() {
|
|
return this._checkModeProperty(constants.S_IFCHR);
|
|
};
|
|
|
|
fs.Stats.prototype.isSymbolicLink = function() {
|
|
return this._checkModeProperty(constants.S_IFLNK);
|
|
};
|
|
|
|
fs.Stats.prototype.isFIFO = function() {
|
|
return this._checkModeProperty(constants.S_IFIFO);
|
|
};
|
|
|
|
fs.Stats.prototype.isSocket = function() {
|
|
return this._checkModeProperty(constants.S_IFSOCK);
|
|
};
|
|
|
|
fs.exists = function(path, callback) {
|
|
if (!nullCheck(path, cb)) return;
|
|
binding.stat(pathModule._makeLong(path), cb);
|
|
function cb(err, stats) {
|
|
if (callback) callback(err ? false : true);
|
|
}
|
|
};
|
|
|
|
fs.existsSync = function(path) {
|
|
try {
|
|
nullCheck(path);
|
|
binding.stat(pathModule._makeLong(path));
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
fs.readFile = function(path, options, callback_) {
|
|
var callback = maybeCallback(arguments[arguments.length - 1]);
|
|
|
|
if (typeof options === 'function' || !options) {
|
|
options = { encoding: null, flag: 'r' };
|
|
} else if (typeof options === 'string') {
|
|
options = { encoding: options, flag: 'r' };
|
|
} else if (!options) {
|
|
options = { encoding: null, flag: 'r' };
|
|
} else if (typeof options !== 'object') {
|
|
throw new TypeError('Bad arguments');
|
|
}
|
|
|
|
var encoding = options.encoding;
|
|
assertEncoding(encoding);
|
|
|
|
// first, stat the file, so we know the size.
|
|
var size;
|
|
var buffer; // single buffer with file data
|
|
var buffers; // list for when size is unknown
|
|
var pos = 0;
|
|
var fd;
|
|
|
|
var flag = options.flag || 'r';
|
|
fs.open(path, flag, 438 /*=0666*/, function(er, fd_) {
|
|
if (er) return callback(er);
|
|
fd = fd_;
|
|
|
|
fs.fstat(fd, function(er, st) {
|
|
if (er) return callback(er);
|
|
size = st.size;
|
|
if (size === 0) {
|
|
// the kernel lies about many files.
|
|
// Go ahead and try to read some bytes.
|
|
buffers = [];
|
|
return read();
|
|
}
|
|
|
|
buffer = new Buffer(size);
|
|
read();
|
|
});
|
|
});
|
|
|
|
function read() {
|
|
if (size === 0) {
|
|
buffer = new Buffer(8192);
|
|
fs.read(fd, buffer, 0, 8192, -1, afterRead);
|
|
} else {
|
|
fs.read(fd, buffer, pos, size - pos, -1, afterRead);
|
|
}
|
|
}
|
|
|
|
function afterRead(er, bytesRead) {
|
|
if (er) {
|
|
return fs.close(fd, function(er2) {
|
|
return callback(er);
|
|
});
|
|
}
|
|
|
|
if (bytesRead === 0) {
|
|
return close();
|
|
}
|
|
|
|
pos += bytesRead;
|
|
if (size !== 0) {
|
|
if (pos === size) close();
|
|
else read();
|
|
} else {
|
|
// unknown size, just read until we don't get bytes.
|
|
buffers.push(buffer.slice(0, bytesRead));
|
|
read();
|
|
}
|
|
}
|
|
|
|
function close() {
|
|
fs.close(fd, function(er) {
|
|
if (size === 0) {
|
|
// collected the data into the buffers list.
|
|
buffer = Buffer.concat(buffers, pos);
|
|
} else if (pos < size) {
|
|
buffer = buffer.slice(0, pos);
|
|
}
|
|
|
|
if (encoding) buffer = buffer.toString(encoding);
|
|
return callback(er, buffer);
|
|
});
|
|
}
|
|
};
|
|
|
|
fs.readFileSync = function(path, options) {
|
|
if (!options) {
|
|
options = { encoding: null, flag: 'r' };
|
|
} else if (typeof options === 'string') {
|
|
options = { encoding: options, flag: 'r' };
|
|
} else if (typeof options !== 'object') {
|
|
throw new TypeError('Bad arguments');
|
|
}
|
|
|
|
var encoding = options.encoding;
|
|
assertEncoding(encoding);
|
|
|
|
var flag = options.flag || 'r';
|
|
var fd = fs.openSync(path, flag, 438 /*=0666*/);
|
|
|
|
var size;
|
|
var threw = true;
|
|
try {
|
|
size = fs.fstatSync(fd).size;
|
|
threw = false;
|
|
} finally {
|
|
if (threw) fs.closeSync(fd);
|
|
}
|
|
|
|
var pos = 0;
|
|
var buffer; // single buffer with file data
|
|
var buffers; // list for when size is unknown
|
|
|
|
if (size === 0) {
|
|
buffers = [];
|
|
} else {
|
|
buffer = new Buffer(size);
|
|
}
|
|
|
|
var done = false;
|
|
while (!done) {
|
|
var threw = true;
|
|
try {
|
|
if (size !== 0) {
|
|
var bytesRead = fs.readSync(fd, buffer, pos, size - pos);
|
|
} else {
|
|
// the kernel lies about many files.
|
|
// Go ahead and try to read some bytes.
|
|
buffer = new Buffer(8192);
|
|
var bytesRead = fs.readSync(fd, buffer, 0, 8192);
|
|
if (bytesRead) {
|
|
buffers.push(buffer.slice(0, bytesRead));
|
|
}
|
|
}
|
|
threw = false;
|
|
} finally {
|
|
if (threw) fs.closeSync(fd);
|
|
}
|
|
|
|
pos += bytesRead;
|
|
done = (bytesRead === 0) || (size !== 0 && pos >= size);
|
|
}
|
|
|
|
fs.closeSync(fd);
|
|
|
|
if (size === 0) {
|
|
// data was collected into the buffers list.
|
|
buffer = Buffer.concat(buffers, pos);
|
|
} else if (pos < size) {
|
|
buffer = buffer.slice(0, pos);
|
|
}
|
|
|
|
if (encoding) buffer = buffer.toString(encoding);
|
|
return buffer;
|
|
};
|
|
|
|
|
|
// Used by binding.open and friends
|
|
function stringToFlags(flag) {
|
|
// Only mess with strings
|
|
if (typeof flag !== 'string') {
|
|
return flag;
|
|
}
|
|
|
|
// O_EXCL is mandated by POSIX, Windows supports it too.
|
|
// Let's add a check anyway, just in case.
|
|
if (!O_EXCL && ~flag.indexOf('x')) {
|
|
throw errnoException('ENOSYS', 'fs.open(O_EXCL)');
|
|
}
|
|
|
|
switch (flag) {
|
|
case 'r' : return O_RDONLY;
|
|
case 'rs' : return O_RDONLY | O_SYNC;
|
|
case 'r+' : return O_RDWR;
|
|
case 'rs+' : return O_RDWR | O_SYNC;
|
|
|
|
case 'w' : return O_TRUNC | O_CREAT | O_WRONLY;
|
|
case 'wx' : // fall through
|
|
case 'xw' : return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL;
|
|
|
|
case 'w+' : return O_TRUNC | O_CREAT | O_RDWR;
|
|
case 'wx+': // fall through
|
|
case 'xw+': return O_TRUNC | O_CREAT | O_RDWR | O_EXCL;
|
|
|
|
case 'a' : return O_APPEND | O_CREAT | O_WRONLY;
|
|
case 'ax' : // fall through
|
|
case 'xa' : return O_APPEND | O_CREAT | O_WRONLY | O_EXCL;
|
|
|
|
case 'a+' : return O_APPEND | O_CREAT | O_RDWR;
|
|
case 'ax+': // fall through
|
|
case 'xa+': return O_APPEND | O_CREAT | O_RDWR | O_EXCL;
|
|
}
|
|
|
|
throw new Error('Unknown file open flag: ' + flag);
|
|
}
|
|
|
|
// exported but hidden, only used by test/simple/test-fs-open-flags.js
|
|
Object.defineProperty(exports, '_stringToFlags', {
|
|
enumerable: false,
|
|
value: stringToFlags
|
|
});
|
|
|
|
|
|
// Yes, the follow could be easily DRYed up but I provide the explicit
|
|
// list to make the arguments clear.
|
|
|
|
fs.close = function(fd, callback) {
|
|
binding.close(fd, makeCallback(callback));
|
|
};
|
|
|
|
fs.closeSync = function(fd) {
|
|
return binding.close(fd);
|
|
};
|
|
|
|
function modeNum(m, def) {
|
|
switch (typeof m) {
|
|
case 'number': return m;
|
|
case 'string': return parseInt(m, 8);
|
|
default:
|
|
if (def) {
|
|
return modeNum(def);
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
fs.open = function(path, flags, mode, callback) {
|
|
callback = makeCallback(arguments[arguments.length - 1]);
|
|
mode = modeNum(mode, 438 /*=0666*/);
|
|
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.open(pathModule._makeLong(path),
|
|
stringToFlags(flags),
|
|
mode,
|
|
callback);
|
|
};
|
|
|
|
fs.openSync = function(path, flags, mode) {
|
|
mode = modeNum(mode, 438 /*=0666*/);
|
|
nullCheck(path);
|
|
return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
|
|
};
|
|
|
|
fs.read = function(fd, buffer, offset, length, position, callback) {
|
|
if (!Buffer.isBuffer(buffer)) {
|
|
// legacy string interface (fd, length, position, encoding, callback)
|
|
var cb = arguments[4],
|
|
encoding = arguments[3];
|
|
|
|
assertEncoding(encoding);
|
|
|
|
position = arguments[2];
|
|
length = arguments[1];
|
|
buffer = new Buffer(length);
|
|
offset = 0;
|
|
|
|
callback = function(err, bytesRead) {
|
|
if (!cb) return;
|
|
|
|
var str = (bytesRead > 0) ? buffer.toString(encoding, 0, bytesRead) : '';
|
|
|
|
(cb)(err, str, bytesRead);
|
|
};
|
|
}
|
|
|
|
function wrapper(err, bytesRead) {
|
|
// Retain a reference to buffer so that it can't be GC'ed too soon.
|
|
callback && callback(err, bytesRead || 0, buffer);
|
|
}
|
|
|
|
binding.read(fd, buffer, offset, length, position, wrapper);
|
|
};
|
|
|
|
fs.readSync = function(fd, buffer, offset, length, position) {
|
|
var legacy = false;
|
|
if (!Buffer.isBuffer(buffer)) {
|
|
// legacy string interface (fd, length, position, encoding, callback)
|
|
legacy = true;
|
|
var encoding = arguments[3];
|
|
|
|
assertEncoding(encoding);
|
|
|
|
position = arguments[2];
|
|
length = arguments[1];
|
|
buffer = new Buffer(length);
|
|
|
|
offset = 0;
|
|
}
|
|
|
|
var r = binding.read(fd, buffer, offset, length, position);
|
|
if (!legacy) {
|
|
return r;
|
|
}
|
|
|
|
var str = (r > 0) ? buffer.toString(encoding, 0, r) : '';
|
|
return [str, r];
|
|
};
|
|
|
|
fs.write = function(fd, buffer, offset, length, position, callback) {
|
|
if (!Buffer.isBuffer(buffer)) {
|
|
// legacy string interface (fd, data, position, encoding, callback)
|
|
callback = arguments[4];
|
|
position = arguments[2];
|
|
assertEncoding(arguments[3]);
|
|
|
|
buffer = new Buffer('' + arguments[1], arguments[3]);
|
|
offset = 0;
|
|
length = buffer.length;
|
|
}
|
|
|
|
if (!length) {
|
|
if (typeof callback == 'function') {
|
|
process.nextTick(function() {
|
|
callback(undefined, 0);
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
|
|
callback = maybeCallback(callback);
|
|
|
|
function wrapper(err, written) {
|
|
// Retain a reference to buffer so that it can't be GC'ed too soon.
|
|
callback(err, written || 0, buffer);
|
|
}
|
|
|
|
binding.write(fd, buffer, offset, length, position, wrapper);
|
|
};
|
|
|
|
fs.writeSync = function(fd, buffer, offset, length, position) {
|
|
if (!Buffer.isBuffer(buffer)) {
|
|
// legacy string interface (fd, data, position, encoding)
|
|
position = arguments[2];
|
|
assertEncoding(arguments[3]);
|
|
|
|
buffer = new Buffer('' + arguments[1], arguments[3]);
|
|
offset = 0;
|
|
length = buffer.length;
|
|
}
|
|
if (!length) return 0;
|
|
|
|
return binding.write(fd, buffer, offset, length, position);
|
|
};
|
|
|
|
fs.rename = function(oldPath, newPath, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(oldPath, callback)) return;
|
|
if (!nullCheck(newPath, callback)) return;
|
|
binding.rename(pathModule._makeLong(oldPath),
|
|
pathModule._makeLong(newPath),
|
|
callback);
|
|
};
|
|
|
|
fs.renameSync = function(oldPath, newPath) {
|
|
nullCheck(oldPath);
|
|
nullCheck(newPath);
|
|
return binding.rename(pathModule._makeLong(oldPath),
|
|
pathModule._makeLong(newPath));
|
|
};
|
|
|
|
fs.truncate = function(path, len, callback) {
|
|
if (typeof path === 'number') {
|
|
// legacy
|
|
return fs.ftruncate(path, len, callback);
|
|
}
|
|
if (typeof len === 'function') {
|
|
callback = len;
|
|
len = 0;
|
|
} else if (typeof len === 'undefined') {
|
|
len = 0;
|
|
}
|
|
callback = maybeCallback(callback);
|
|
fs.open(path, 'w', function(er, fd) {
|
|
if (er) return callback(er);
|
|
binding.ftruncate(fd, len, function(er) {
|
|
fs.close(fd, function(er2) {
|
|
callback(er || er2);
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
fs.truncateSync = function(path, len) {
|
|
if (typeof path === 'number') {
|
|
// legacy
|
|
return fs.ftruncateSync(path, len);
|
|
}
|
|
if (typeof len === 'undefined') {
|
|
len = 0;
|
|
}
|
|
// allow error to be thrown, but still close fd.
|
|
var fd = fs.openSync(path, 'w');
|
|
try {
|
|
var ret = fs.ftruncateSync(fd, len);
|
|
} finally {
|
|
fs.closeSync(fd);
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
fs.ftruncate = function(fd, len, callback) {
|
|
if (typeof len === 'function') {
|
|
callback = len;
|
|
len = 0;
|
|
} else if (typeof len === 'undefined') {
|
|
len = 0;
|
|
}
|
|
binding.ftruncate(fd, len, makeCallback(callback));
|
|
};
|
|
|
|
fs.ftruncateSync = function(fd, len) {
|
|
if (typeof len === 'undefined') {
|
|
len = 0;
|
|
}
|
|
return binding.ftruncate(fd, len);
|
|
};
|
|
|
|
fs.rmdir = function(path, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.rmdir(pathModule._makeLong(path), callback);
|
|
};
|
|
|
|
fs.rmdirSync = function(path) {
|
|
nullCheck(path);
|
|
return binding.rmdir(pathModule._makeLong(path));
|
|
};
|
|
|
|
fs.fdatasync = function(fd, callback) {
|
|
binding.fdatasync(fd, makeCallback(callback));
|
|
};
|
|
|
|
fs.fdatasyncSync = function(fd) {
|
|
return binding.fdatasync(fd);
|
|
};
|
|
|
|
fs.fsync = function(fd, callback) {
|
|
binding.fsync(fd, makeCallback(callback));
|
|
};
|
|
|
|
fs.fsyncSync = function(fd) {
|
|
return binding.fsync(fd);
|
|
};
|
|
|
|
fs.mkdir = function(path, mode, callback) {
|
|
if (typeof mode === 'function') callback = mode;
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.mkdir(pathModule._makeLong(path),
|
|
modeNum(mode, 511 /*=0777*/),
|
|
callback);
|
|
};
|
|
|
|
fs.mkdirSync = function(path, mode) {
|
|
nullCheck(path);
|
|
return binding.mkdir(pathModule._makeLong(path),
|
|
modeNum(mode, 511 /*=0777*/));
|
|
};
|
|
|
|
fs.readdir = function(path, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.readdir(pathModule._makeLong(path), callback);
|
|
};
|
|
|
|
fs.readdirSync = function(path) {
|
|
nullCheck(path);
|
|
return binding.readdir(pathModule._makeLong(path));
|
|
};
|
|
|
|
fs.fstat = function(fd, callback) {
|
|
binding.fstat(fd, makeCallback(callback));
|
|
};
|
|
|
|
fs.lstat = function(path, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.lstat(pathModule._makeLong(path), callback);
|
|
};
|
|
|
|
fs.stat = function(path, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.stat(pathModule._makeLong(path), callback);
|
|
};
|
|
|
|
fs.fstatSync = function(fd) {
|
|
return binding.fstat(fd);
|
|
};
|
|
|
|
fs.lstatSync = function(path) {
|
|
nullCheck(path);
|
|
return binding.lstat(pathModule._makeLong(path));
|
|
};
|
|
|
|
fs.statSync = function(path) {
|
|
nullCheck(path);
|
|
return binding.stat(pathModule._makeLong(path));
|
|
};
|
|
|
|
fs.readlink = function(path, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.readlink(pathModule._makeLong(path), callback);
|
|
};
|
|
|
|
fs.readlinkSync = function(path) {
|
|
nullCheck(path);
|
|
return binding.readlink(pathModule._makeLong(path));
|
|
};
|
|
|
|
function preprocessSymlinkDestination(path, type) {
|
|
if (!isWindows) {
|
|
// No preprocessing is needed on Unix.
|
|
return path;
|
|
} else if (type === 'junction') {
|
|
// Junctions paths need to be absolute and \\?\-prefixed.
|
|
return pathModule._makeLong(path);
|
|
} else {
|
|
// Windows symlinks don't tolerate forward slashes.
|
|
return ('' + path).replace(/\//g, '\\');
|
|
}
|
|
}
|
|
|
|
fs.symlink = function(destination, path, type_, callback) {
|
|
var type = (typeof type_ === 'string' ? type_ : null);
|
|
var callback = makeCallback(arguments[arguments.length - 1]);
|
|
|
|
if (!nullCheck(destination, callback)) return;
|
|
if (!nullCheck(path, callback)) return;
|
|
|
|
binding.symlink(preprocessSymlinkDestination(destination, type),
|
|
pathModule._makeLong(path),
|
|
type,
|
|
callback);
|
|
};
|
|
|
|
fs.symlinkSync = function(destination, path, type) {
|
|
type = (typeof type === 'string' ? type : null);
|
|
|
|
nullCheck(destination);
|
|
nullCheck(path);
|
|
|
|
return binding.symlink(preprocessSymlinkDestination(destination, type),
|
|
pathModule._makeLong(path),
|
|
type);
|
|
};
|
|
|
|
fs.link = function(srcpath, dstpath, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(srcpath, callback)) return;
|
|
if (!nullCheck(dstpath, callback)) return;
|
|
|
|
binding.link(pathModule._makeLong(srcpath),
|
|
pathModule._makeLong(dstpath),
|
|
callback);
|
|
};
|
|
|
|
fs.linkSync = function(srcpath, dstpath) {
|
|
nullCheck(srcpath);
|
|
nullCheck(dstpath);
|
|
return binding.link(pathModule._makeLong(srcpath),
|
|
pathModule._makeLong(dstpath));
|
|
};
|
|
|
|
fs.unlink = function(path, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.unlink(pathModule._makeLong(path), callback);
|
|
};
|
|
|
|
fs.unlinkSync = function(path) {
|
|
nullCheck(path);
|
|
return binding.unlink(pathModule._makeLong(path));
|
|
};
|
|
|
|
fs.fchmod = function(fd, mode, callback) {
|
|
binding.fchmod(fd, modeNum(mode), makeCallback(callback));
|
|
};
|
|
|
|
fs.fchmodSync = function(fd, mode) {
|
|
return binding.fchmod(fd, modeNum(mode));
|
|
};
|
|
|
|
if (constants.hasOwnProperty('O_SYMLINK')) {
|
|
fs.lchmod = function(path, mode, callback) {
|
|
callback = maybeCallback(callback);
|
|
fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
|
|
if (err) {
|
|
callback(err);
|
|
return;
|
|
}
|
|
// prefer to return the chmod error, if one occurs,
|
|
// but still try to close, and report closing errors if they occur.
|
|
fs.fchmod(fd, mode, function(err) {
|
|
fs.close(fd, function(err2) {
|
|
callback(err || err2);
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
fs.lchmodSync = function(path, mode) {
|
|
var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK);
|
|
|
|
// prefer to return the chmod error, if one occurs,
|
|
// but still try to close, and report closing errors if they occur.
|
|
var err, err2;
|
|
try {
|
|
var ret = fs.fchmodSync(fd, mode);
|
|
} catch (er) {
|
|
err = er;
|
|
}
|
|
try {
|
|
fs.closeSync(fd);
|
|
} catch (er) {
|
|
err2 = er;
|
|
}
|
|
if (err || err2) throw (err || err2);
|
|
return ret;
|
|
};
|
|
}
|
|
|
|
|
|
fs.chmod = function(path, mode, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.chmod(pathModule._makeLong(path),
|
|
modeNum(mode),
|
|
callback);
|
|
};
|
|
|
|
fs.chmodSync = function(path, mode) {
|
|
nullCheck(path);
|
|
return binding.chmod(pathModule._makeLong(path), modeNum(mode));
|
|
};
|
|
|
|
if (constants.hasOwnProperty('O_SYMLINK')) {
|
|
fs.lchown = function(path, uid, gid, callback) {
|
|
callback = maybeCallback(callback);
|
|
fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
|
|
if (err) {
|
|
callback(err);
|
|
return;
|
|
}
|
|
fs.fchown(fd, uid, gid, callback);
|
|
});
|
|
};
|
|
|
|
fs.lchownSync = function(path, uid, gid) {
|
|
var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK);
|
|
return fs.fchownSync(fd, uid, gid);
|
|
};
|
|
}
|
|
|
|
fs.fchown = function(fd, uid, gid, callback) {
|
|
binding.fchown(fd, uid, gid, makeCallback(callback));
|
|
};
|
|
|
|
fs.fchownSync = function(fd, uid, gid) {
|
|
return binding.fchown(fd, uid, gid);
|
|
};
|
|
|
|
fs.chown = function(path, uid, gid, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.chown(pathModule._makeLong(path), uid, gid, callback);
|
|
};
|
|
|
|
fs.chownSync = function(path, uid, gid) {
|
|
nullCheck(path);
|
|
return binding.chown(pathModule._makeLong(path), uid, gid);
|
|
};
|
|
|
|
// converts Date or number to a fractional UNIX timestamp
|
|
function toUnixTimestamp(time) {
|
|
if (typeof time == 'number') {
|
|
return time;
|
|
}
|
|
if (time instanceof Date) {
|
|
// convert to 123.456 UNIX timestamp
|
|
return time.getTime() / 1000;
|
|
}
|
|
throw new Error('Cannot parse time: ' + time);
|
|
}
|
|
|
|
// exported for unit tests, not for public consumption
|
|
fs._toUnixTimestamp = toUnixTimestamp;
|
|
|
|
fs.utimes = function(path, atime, mtime, callback) {
|
|
callback = makeCallback(callback);
|
|
if (!nullCheck(path, callback)) return;
|
|
binding.utimes(pathModule._makeLong(path),
|
|
toUnixTimestamp(atime),
|
|
toUnixTimestamp(mtime),
|
|
callback);
|
|
};
|
|
|
|
fs.utimesSync = function(path, atime, mtime) {
|
|
nullCheck(path);
|
|
atime = toUnixTimestamp(atime);
|
|
mtime = toUnixTimestamp(mtime);
|
|
binding.utimes(pathModule._makeLong(path), atime, mtime);
|
|
};
|
|
|
|
fs.futimes = function(fd, atime, mtime, callback) {
|
|
atime = toUnixTimestamp(atime);
|
|
mtime = toUnixTimestamp(mtime);
|
|
binding.futimes(fd, atime, mtime, makeCallback(callback));
|
|
};
|
|
|
|
fs.futimesSync = function(fd, atime, mtime) {
|
|
atime = toUnixTimestamp(atime);
|
|
mtime = toUnixTimestamp(mtime);
|
|
binding.futimes(fd, atime, mtime);
|
|
};
|
|
|
|
function writeAll(fd, buffer, offset, length, position, callback) {
|
|
callback = maybeCallback(arguments[arguments.length - 1]);
|
|
|
|
// write(fd, buffer, offset, length, position, callback)
|
|
fs.write(fd, buffer, offset, length, position, function(writeErr, written) {
|
|
if (writeErr) {
|
|
fs.close(fd, function() {
|
|
if (callback) callback(writeErr);
|
|
});
|
|
} else {
|
|
if (written === length) {
|
|
fs.close(fd, callback);
|
|
} else {
|
|
offset += written;
|
|
length -= written;
|
|
position += written;
|
|
writeAll(fd, buffer, offset, length, position, callback);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
fs.writeFile = function(path, data, options, callback) {
|
|
var callback = maybeCallback(arguments[arguments.length - 1]);
|
|
|
|
if (typeof options === 'function' || !options) {
|
|
options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' };
|
|
} else if (typeof options === 'string') {
|
|
options = { encoding: options, mode: 438, flag: 'w' };
|
|
} else if (!options) {
|
|
options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' };
|
|
} else if (typeof options !== 'object') {
|
|
throw new TypeError('Bad arguments');
|
|
}
|
|
|
|
assertEncoding(options.encoding);
|
|
|
|
var flag = options.flag || 'w';
|
|
fs.open(path, options.flag || 'w', options.mode, function(openErr, fd) {
|
|
if (openErr) {
|
|
if (callback) callback(openErr);
|
|
} else {
|
|
var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data,
|
|
options.encoding || 'utf8');
|
|
var position = /a/.test(flag) ? null : 0;
|
|
writeAll(fd, buffer, 0, buffer.length, position, callback);
|
|
}
|
|
});
|
|
};
|
|
|
|
fs.writeFileSync = function(path, data, options) {
|
|
if (!options) {
|
|
options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' };
|
|
} else if (typeof options === 'string') {
|
|
options = { encoding: options, mode: 438, flag: 'w' };
|
|
} else if (typeof options !== 'object') {
|
|
throw new TypeError('Bad arguments');
|
|
}
|
|
|
|
assertEncoding(options.encoding);
|
|
|
|
var flag = options.flag || 'w';
|
|
var fd = fs.openSync(path, flag);
|
|
if (!Buffer.isBuffer(data)) {
|
|
data = new Buffer('' + data, options.encoding || 'utf8');
|
|
}
|
|
var written = 0;
|
|
var length = data.length;
|
|
var position = /a/.test(flag) ? null : 0;
|
|
try {
|
|
while (written < length) {
|
|
written += fs.writeSync(fd, data, written, length - written, position);
|
|
position += written;
|
|
}
|
|
} finally {
|
|
fs.closeSync(fd);
|
|
}
|
|
};
|
|
|
|
fs.appendFile = function(path, data, options, callback_) {
|
|
var callback = maybeCallback(arguments[arguments.length - 1]);
|
|
|
|
if (typeof options === 'function' || !options) {
|
|
options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' };
|
|
} else if (typeof options === 'string') {
|
|
options = { encoding: options, mode: 438, flag: 'a' };
|
|
} else if (!options) {
|
|
options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' };
|
|
} else if (typeof options !== 'object') {
|
|
throw new TypeError('Bad arguments');
|
|
}
|
|
|
|
if (!options.flag)
|
|
options = util._extend({ flag: 'a' }, options);
|
|
fs.writeFile(path, data, options, callback);
|
|
};
|
|
|
|
fs.appendFileSync = function(path, data, options) {
|
|
if (!options) {
|
|
options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' };
|
|
} else if (typeof options === 'string') {
|
|
options = { encoding: options, mode: 438, flag: 'a' };
|
|
} else if (typeof options !== 'object') {
|
|
throw new TypeError('Bad arguments');
|
|
}
|
|
if (!options.flag)
|
|
options = util._extend({ flag: 'a' }, options);
|
|
|
|
fs.writeFileSync(path, data, options);
|
|
};
|
|
|
|
function errnoException(errorno, syscall) {
|
|
// TODO make this more compatible with ErrnoException from src/node.cc
|
|
// Once all of Node is using this function the ErrnoException from
|
|
// src/node.cc should be removed.
|
|
var e = new Error(syscall + ' ' + errorno);
|
|
e.errno = e.code = errorno;
|
|
e.syscall = syscall;
|
|
return e;
|
|
}
|
|
|
|
|
|
function FSWatcher() {
|
|
EventEmitter.call(this);
|
|
|
|
var self = this;
|
|
var FSEvent = process.binding('fs_event_wrap').FSEvent;
|
|
this._handle = new FSEvent();
|
|
this._handle.owner = this;
|
|
|
|
this._handle.onchange = function(status, event, filename) {
|
|
if (status) {
|
|
self._handle.close();
|
|
self.emit('error', errnoException(process._errno, 'watch'));
|
|
} else {
|
|
self.emit('change', event, filename);
|
|
}
|
|
};
|
|
}
|
|
util.inherits(FSWatcher, EventEmitter);
|
|
|
|
FSWatcher.prototype.start = function(filename, persistent) {
|
|
nullCheck(filename);
|
|
var r = this._handle.start(pathModule._makeLong(filename), persistent);
|
|
|
|
if (r) {
|
|
this._handle.close();
|
|
throw errnoException(process._errno, 'watch');
|
|
}
|
|
};
|
|
|
|
FSWatcher.prototype.close = function() {
|
|
this._handle.close();
|
|
};
|
|
|
|
fs.watch = function(filename) {
|
|
nullCheck(filename);
|
|
var watcher;
|
|
var options;
|
|
var listener;
|
|
|
|
if ('object' == typeof arguments[1]) {
|
|
options = arguments[1];
|
|
listener = arguments[2];
|
|
} else {
|
|
options = {};
|
|
listener = arguments[1];
|
|
}
|
|
|
|
if (options.persistent === undefined) options.persistent = true;
|
|
|
|
watcher = new FSWatcher();
|
|
watcher.start(filename, options.persistent);
|
|
|
|
if (listener) {
|
|
watcher.addListener('change', listener);
|
|
}
|
|
|
|
return watcher;
|
|
};
|
|
|
|
|
|
// Stat Change Watchers
|
|
|
|
function StatWatcher() {
|
|
EventEmitter.call(this);
|
|
|
|
var self = this;
|
|
this._handle = new binding.StatWatcher();
|
|
|
|
// uv_fs_poll is a little more powerful than ev_stat but we curb it for
|
|
// the sake of backwards compatibility
|
|
var oldStatus = -1;
|
|
|
|
this._handle.onchange = function(current, previous, newStatus) {
|
|
if (oldStatus === -1 &&
|
|
newStatus === -1 &&
|
|
current.nlink === previous.nlink) return;
|
|
|
|
oldStatus = newStatus;
|
|
self.emit('change', current, previous);
|
|
};
|
|
|
|
this._handle.onstop = function() {
|
|
self.emit('stop');
|
|
};
|
|
}
|
|
util.inherits(StatWatcher, EventEmitter);
|
|
|
|
|
|
StatWatcher.prototype.start = function(filename, persistent, interval) {
|
|
nullCheck(filename);
|
|
this._handle.start(pathModule._makeLong(filename), persistent, interval);
|
|
};
|
|
|
|
|
|
StatWatcher.prototype.stop = function() {
|
|
this._handle.stop();
|
|
};
|
|
|
|
|
|
var statWatchers = {};
|
|
function inStatWatchers(filename) {
|
|
return Object.prototype.hasOwnProperty.call(statWatchers, filename) &&
|
|
statWatchers[filename];
|
|
}
|
|
|
|
|
|
fs.watchFile = function(filename) {
|
|
nullCheck(filename);
|
|
var stat;
|
|
var listener;
|
|
|
|
var options = {
|
|
// Poll interval in milliseconds. 5007 is what libev used to use. It's
|
|
// a little on the slow side but let's stick with it for now to keep
|
|
// behavioral changes to a minimum.
|
|
interval: 5007,
|
|
persistent: true
|
|
};
|
|
|
|
if ('object' == typeof arguments[1]) {
|
|
options = util._extend(options, arguments[1]);
|
|
listener = arguments[2];
|
|
} else {
|
|
listener = arguments[1];
|
|
}
|
|
|
|
if (!listener) {
|
|
throw new Error('watchFile requires a listener function');
|
|
}
|
|
|
|
if (inStatWatchers(filename)) {
|
|
stat = statWatchers[filename];
|
|
} else {
|
|
stat = statWatchers[filename] = new StatWatcher();
|
|
stat.start(filename, options.persistent, options.interval);
|
|
}
|
|
stat.addListener('change', listener);
|
|
return stat;
|
|
};
|
|
|
|
fs.unwatchFile = function(filename, listener) {
|
|
nullCheck(filename);
|
|
if (!inStatWatchers(filename)) return;
|
|
|
|
var stat = statWatchers[filename];
|
|
|
|
if (typeof listener === 'function') {
|
|
stat.removeListener('change', listener);
|
|
} else {
|
|
stat.removeAllListeners('change');
|
|
}
|
|
|
|
if (EventEmitter.listenerCount(stat, 'change') === 0) {
|
|
stat.stop();
|
|
statWatchers[filename] = undefined;
|
|
}
|
|
};
|
|
|
|
// Realpath
|
|
// Not using realpath(2) because it's bad.
|
|
// See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
|
|
|
|
var normalize = pathModule.normalize;
|
|
|
|
// Regexp that finds the next partion of a (partial) path
|
|
// result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
|
|
if (isWindows) {
|
|
var nextPartRe = /(.*?)(?:[\/\\]+|$)/g;
|
|
} else {
|
|
var nextPartRe = /(.*?)(?:[\/]+|$)/g;
|
|
}
|
|
|
|
// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
|
|
if (isWindows) {
|
|
var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/;
|
|
} else {
|
|
var splitRootRe = /^[\/]*/;
|
|
}
|
|
|
|
fs.realpathSync = function realpathSync(p, cache) {
|
|
// make p is absolute
|
|
p = pathModule.resolve(p);
|
|
|
|
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
|
|
return cache[p];
|
|
}
|
|
|
|
var original = p,
|
|
seenLinks = {},
|
|
knownHard = {};
|
|
|
|
// current character position in p
|
|
var pos;
|
|
// the partial path so far, including a trailing slash if any
|
|
var current;
|
|
// the partial path without a trailing slash (except when pointing at a root)
|
|
var base;
|
|
// the partial path scanned in the previous round, with slash
|
|
var previous;
|
|
|
|
start();
|
|
|
|
function start() {
|
|
// Skip over roots
|
|
var m = splitRootRe.exec(p);
|
|
pos = m[0].length;
|
|
current = m[0];
|
|
base = m[0];
|
|
previous = '';
|
|
|
|
// On windows, check that the root exists. On unix there is no need.
|
|
if (isWindows && !knownHard[base]) {
|
|
fs.lstatSync(base);
|
|
knownHard[base] = true;
|
|
}
|
|
}
|
|
|
|
// walk down the path, swapping out linked pathparts for their real
|
|
// values
|
|
// NB: p.length changes.
|
|
while (pos < p.length) {
|
|
// find the next part
|
|
nextPartRe.lastIndex = pos;
|
|
var result = nextPartRe.exec(p);
|
|
previous = current;
|
|
current += result[0];
|
|
base = previous + result[1];
|
|
pos = nextPartRe.lastIndex;
|
|
|
|
// continue if not a symlink
|
|
if (knownHard[base] || (cache && cache[base] === base)) {
|
|
continue;
|
|
}
|
|
|
|
var resolvedLink;
|
|
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
|
|
// some known symbolic link. no need to stat again.
|
|
resolvedLink = cache[base];
|
|
} else {
|
|
var stat = fs.lstatSync(base);
|
|
if (!stat.isSymbolicLink()) {
|
|
knownHard[base] = true;
|
|
if (cache) cache[base] = base;
|
|
continue;
|
|
}
|
|
|
|
// read the link if it wasn't read before
|
|
// dev/ino always return 0 on windows, so skip the check.
|
|
var linkTarget = null;
|
|
if (!isWindows) {
|
|
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
|
if (seenLinks.hasOwnProperty(id)) {
|
|
linkTarget = seenLinks[id];
|
|
}
|
|
}
|
|
if (linkTarget === null) {
|
|
fs.statSync(base);
|
|
linkTarget = fs.readlinkSync(base);
|
|
}
|
|
resolvedLink = pathModule.resolve(previous, linkTarget);
|
|
// track this, if given a cache.
|
|
if (cache) cache[base] = resolvedLink;
|
|
if (!isWindows) seenLinks[id] = linkTarget;
|
|
}
|
|
|
|
// resolve the link, then start over
|
|
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
|
start();
|
|
}
|
|
|
|
if (cache) cache[original] = p;
|
|
|
|
return p;
|
|
};
|
|
|
|
|
|
fs.realpath = function realpath(p, cache, cb) {
|
|
if (typeof cb !== 'function') {
|
|
cb = maybeCallback(cache);
|
|
cache = null;
|
|
}
|
|
|
|
// make p is absolute
|
|
p = pathModule.resolve(p);
|
|
|
|
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
|
|
return process.nextTick(cb.bind(null, null, cache[p]));
|
|
}
|
|
|
|
var original = p,
|
|
seenLinks = {},
|
|
knownHard = {};
|
|
|
|
// current character position in p
|
|
var pos;
|
|
// the partial path so far, including a trailing slash if any
|
|
var current;
|
|
// the partial path without a trailing slash (except when pointing at a root)
|
|
var base;
|
|
// the partial path scanned in the previous round, with slash
|
|
var previous;
|
|
|
|
start();
|
|
|
|
function start() {
|
|
// Skip over roots
|
|
var m = splitRootRe.exec(p);
|
|
pos = m[0].length;
|
|
current = m[0];
|
|
base = m[0];
|
|
previous = '';
|
|
|
|
// On windows, check that the root exists. On unix there is no need.
|
|
if (isWindows && !knownHard[base]) {
|
|
fs.lstat(base, function(err) {
|
|
if (err) return cb(err);
|
|
knownHard[base] = true;
|
|
LOOP();
|
|
});
|
|
} else {
|
|
process.nextTick(LOOP);
|
|
}
|
|
}
|
|
|
|
// walk down the path, swapping out linked pathparts for their real
|
|
// values
|
|
function LOOP() {
|
|
// stop if scanned past end of path
|
|
if (pos >= p.length) {
|
|
if (cache) cache[original] = p;
|
|
return cb(null, p);
|
|
}
|
|
|
|
// find the next part
|
|
nextPartRe.lastIndex = pos;
|
|
var result = nextPartRe.exec(p);
|
|
previous = current;
|
|
current += result[0];
|
|
base = previous + result[1];
|
|
pos = nextPartRe.lastIndex;
|
|
|
|
// continue if not a symlink
|
|
if (knownHard[base] || (cache && cache[base] === base)) {
|
|
return process.nextTick(LOOP);
|
|
}
|
|
|
|
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
|
|
// known symbolic link. no need to stat again.
|
|
return gotResolvedLink(cache[base]);
|
|
}
|
|
|
|
return fs.lstat(base, gotStat);
|
|
}
|
|
|
|
function gotStat(err, stat) {
|
|
if (err) return cb(err);
|
|
|
|
// if not a symlink, skip to the next path part
|
|
if (!stat.isSymbolicLink()) {
|
|
knownHard[base] = true;
|
|
if (cache) cache[base] = base;
|
|
return process.nextTick(LOOP);
|
|
}
|
|
|
|
// stat & read the link if not read before
|
|
// call gotTarget as soon as the link target is known
|
|
// dev/ino always return 0 on windows, so skip the check.
|
|
if (!isWindows) {
|
|
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
|
if (seenLinks.hasOwnProperty(id)) {
|
|
return gotTarget(null, seenLinks[id], base);
|
|
}
|
|
}
|
|
fs.stat(base, function(err) {
|
|
if (err) return cb(err);
|
|
|
|
fs.readlink(base, function(err, target) {
|
|
if (!isWindows) seenLinks[id] = target;
|
|
gotTarget(err, target);
|
|
});
|
|
});
|
|
}
|
|
|
|
function gotTarget(err, target, base) {
|
|
if (err) return cb(err);
|
|
|
|
var resolvedLink = pathModule.resolve(previous, target);
|
|
if (cache) cache[base] = resolvedLink;
|
|
gotResolvedLink(resolvedLink);
|
|
}
|
|
|
|
function gotResolvedLink(resolvedLink) {
|
|
// resolve the link, then start over
|
|
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
|
start();
|
|
}
|
|
};
|
|
|
|
|
|
|
|
var pool;
|
|
|
|
function allocNewPool(poolSize) {
|
|
pool = new Buffer(poolSize);
|
|
pool.used = 0;
|
|
}
|
|
|
|
|
|
|
|
fs.createReadStream = function(path, options) {
|
|
return new ReadStream(path, options);
|
|
};
|
|
|
|
util.inherits(ReadStream, Readable);
|
|
fs.ReadStream = ReadStream;
|
|
|
|
function ReadStream(path, options) {
|
|
if (!(this instanceof ReadStream))
|
|
return new ReadStream(path, options);
|
|
|
|
// a little bit bigger buffer and water marks by default
|
|
options = util._extend({
|
|
highWaterMark: 64 * 1024
|
|
}, options || {});
|
|
|
|
Readable.call(this, options);
|
|
|
|
this.path = path;
|
|
this.fd = options.hasOwnProperty('fd') ? options.fd : null;
|
|
this.flags = options.hasOwnProperty('flags') ? options.flags : 'r';
|
|
this.mode = options.hasOwnProperty('mode') ? options.mode : 438; /*=0666*/
|
|
|
|
this.start = options.hasOwnProperty('start') ? options.start : undefined;
|
|
this.end = options.hasOwnProperty('end') ? options.end : undefined;
|
|
this.autoClose = options.hasOwnProperty('autoClose') ?
|
|
options.autoClose : true;
|
|
this.pos = undefined;
|
|
|
|
if (this.start !== undefined) {
|
|
if ('number' !== typeof this.start) {
|
|
throw TypeError('start must be a Number');
|
|
}
|
|
if (this.end === undefined) {
|
|
this.end = Infinity;
|
|
} else if ('number' !== typeof this.end) {
|
|
throw TypeError('end must be a Number');
|
|
}
|
|
|
|
if (this.start > this.end) {
|
|
throw new Error('start must be <= end');
|
|
}
|
|
|
|
this.pos = this.start;
|
|
}
|
|
|
|
if (typeof this.fd !== 'number')
|
|
this.open();
|
|
|
|
this.on('end', function() {
|
|
if (this.autoClose) {
|
|
this.destroy();
|
|
}
|
|
});
|
|
}
|
|
|
|
fs.FileReadStream = fs.ReadStream; // support the legacy name
|
|
|
|
ReadStream.prototype.open = function() {
|
|
var self = this;
|
|
fs.open(this.path, this.flags, this.mode, function(er, fd) {
|
|
if (er) {
|
|
if (this.autoClose) {
|
|
self.destroy();
|
|
}
|
|
self.emit('error', er);
|
|
return;
|
|
}
|
|
|
|
self.fd = fd;
|
|
self.emit('open', fd);
|
|
// start the flow of data.
|
|
self.read();
|
|
});
|
|
};
|
|
|
|
ReadStream.prototype._read = function(n) {
|
|
if (typeof this.fd !== 'number')
|
|
return this.once('open', function() {
|
|
this._read(n);
|
|
});
|
|
|
|
if (this.destroyed)
|
|
return;
|
|
|
|
if (!pool || pool.length - pool.used < kMinPoolSpace) {
|
|
// discard the old pool.
|
|
pool = null;
|
|
allocNewPool(this._readableState.highWaterMark);
|
|
}
|
|
|
|
// Grab another reference to the pool in the case that while we're
|
|
// in the thread pool another read() finishes up the pool, and
|
|
// allocates a new one.
|
|
var thisPool = pool;
|
|
var toRead = Math.min(pool.length - pool.used, n);
|
|
var start = pool.used;
|
|
|
|
if (this.pos !== undefined)
|
|
toRead = Math.min(this.end - this.pos + 1, toRead);
|
|
|
|
// already read everything we were supposed to read!
|
|
// treat as EOF.
|
|
if (toRead <= 0)
|
|
return this.push(null);
|
|
|
|
// the actual read.
|
|
var self = this;
|
|
fs.read(this.fd, pool, pool.used, toRead, this.pos, onread);
|
|
|
|
// move the pool positions, and internal position for reading.
|
|
if (this.pos !== undefined)
|
|
this.pos += toRead;
|
|
pool.used += toRead;
|
|
|
|
function onread(er, bytesRead) {
|
|
if (er) {
|
|
if (self.autoClose) {
|
|
self.destroy();
|
|
}
|
|
self.emit('error', er);
|
|
} else {
|
|
var b = null;
|
|
if (bytesRead > 0)
|
|
b = thisPool.slice(start, start + bytesRead);
|
|
|
|
self.push(b);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
ReadStream.prototype.destroy = function() {
|
|
if (this.destroyed)
|
|
return;
|
|
this.destroyed = true;
|
|
|
|
if ('number' === typeof this.fd)
|
|
this.close();
|
|
};
|
|
|
|
|
|
ReadStream.prototype.close = function(cb) {
|
|
var self = this;
|
|
if (cb)
|
|
this.once('close', cb);
|
|
if (this.closed || 'number' !== typeof this.fd) {
|
|
if ('number' !== typeof this.fd) {
|
|
this.once('open', close);
|
|
return;
|
|
}
|
|
return process.nextTick(this.emit.bind(this, 'close'));
|
|
}
|
|
this.closed = true;
|
|
close();
|
|
|
|
function close(fd) {
|
|
fs.close(fd || self.fd, function(er) {
|
|
if (er)
|
|
self.emit('error', er);
|
|
else
|
|
self.emit('close');
|
|
});
|
|
self.fd = null;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
fs.createWriteStream = function(path, options) {
|
|
return new WriteStream(path, options);
|
|
};
|
|
|
|
util.inherits(WriteStream, Writable);
|
|
fs.WriteStream = WriteStream;
|
|
function WriteStream(path, options) {
|
|
if (!(this instanceof WriteStream))
|
|
return new WriteStream(path, options);
|
|
|
|
options = options || {};
|
|
|
|
Writable.call(this, options);
|
|
|
|
this.path = path;
|
|
this.fd = null;
|
|
|
|
this.fd = options.hasOwnProperty('fd') ? options.fd : null;
|
|
this.flags = options.hasOwnProperty('flags') ? options.flags : 'w';
|
|
this.mode = options.hasOwnProperty('mode') ? options.mode : 438; /*=0666*/
|
|
|
|
this.start = options.hasOwnProperty('start') ? options.start : undefined;
|
|
this.pos = undefined;
|
|
this.bytesWritten = 0;
|
|
|
|
if (this.start !== undefined) {
|
|
if ('number' !== typeof this.start) {
|
|
throw TypeError('start must be a Number');
|
|
}
|
|
if (this.start < 0) {
|
|
throw new Error('start must be >= zero');
|
|
}
|
|
|
|
this.pos = this.start;
|
|
}
|
|
|
|
if ('number' !== typeof this.fd)
|
|
this.open();
|
|
|
|
// dispose on finish.
|
|
this.once('finish', this.close);
|
|
}
|
|
|
|
fs.FileWriteStream = fs.WriteStream; // support the legacy name
|
|
|
|
|
|
WriteStream.prototype.open = function() {
|
|
fs.open(this.path, this.flags, this.mode, function(er, fd) {
|
|
if (er) {
|
|
this.destroy();
|
|
this.emit('error', er);
|
|
return;
|
|
}
|
|
|
|
this.fd = fd;
|
|
this.emit('open', fd);
|
|
}.bind(this));
|
|
};
|
|
|
|
|
|
WriteStream.prototype._write = function(data, encoding, cb) {
|
|
if (!Buffer.isBuffer(data))
|
|
return this.emit('error', new Error('Invalid data'));
|
|
|
|
if (typeof this.fd !== 'number')
|
|
return this.once('open', function() {
|
|
this._write(data, encoding, cb);
|
|
});
|
|
|
|
var self = this;
|
|
fs.write(this.fd, data, 0, data.length, this.pos, function(er, bytes) {
|
|
if (er) {
|
|
self.destroy();
|
|
return cb(er);
|
|
}
|
|
self.bytesWritten += bytes;
|
|
cb();
|
|
});
|
|
|
|
if (this.pos !== undefined)
|
|
this.pos += data.length;
|
|
};
|
|
|
|
|
|
WriteStream.prototype.destroy = ReadStream.prototype.destroy;
|
|
WriteStream.prototype.close = ReadStream.prototype.close;
|
|
|
|
// There is no shutdown() for files.
|
|
WriteStream.prototype.destroySoon = WriteStream.prototype.end;
|
|
|
|
|
|
// SyncWriteStream is internal. DO NOT USE.
|
|
// Temporary hack for process.stdout and process.stderr when piped to files.
|
|
function SyncWriteStream(fd) {
|
|
Stream.call(this);
|
|
|
|
this.fd = fd;
|
|
this.writable = true;
|
|
this.readable = false;
|
|
}
|
|
|
|
util.inherits(SyncWriteStream, Stream);
|
|
|
|
|
|
// Export
|
|
fs.SyncWriteStream = SyncWriteStream;
|
|
|
|
|
|
SyncWriteStream.prototype.write = function(data, arg1, arg2) {
|
|
var encoding, cb;
|
|
|
|
// parse arguments
|
|
if (arg1) {
|
|
if (typeof arg1 === 'string') {
|
|
encoding = arg1;
|
|
cb = arg2;
|
|
} else if (typeof arg1 === 'function') {
|
|
cb = arg1;
|
|
} else {
|
|
throw new Error('bad arg');
|
|
}
|
|
}
|
|
assertEncoding(encoding);
|
|
|
|
// Change strings to buffers. SLOW
|
|
if (typeof data == 'string') {
|
|
data = new Buffer(data, encoding);
|
|
}
|
|
|
|
fs.writeSync(this.fd, data, 0, data.length);
|
|
|
|
if (cb) {
|
|
process.nextTick(cb);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
|
|
SyncWriteStream.prototype.end = function(data, arg1, arg2) {
|
|
if (data) {
|
|
this.write(data, arg1, arg2);
|
|
}
|
|
this.destroy();
|
|
};
|
|
|
|
|
|
SyncWriteStream.prototype.destroy = function() {
|
|
fs.closeSync(this.fd);
|
|
this.fd = null;
|
|
this.emit('close');
|
|
return true;
|
|
};
|
|
|
|
SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy;
|