Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

fs and zlib

  • Loading branch information...
commit bbf9ce2786ac79f8144ac35f1ffe0e8b775c5046 1 parent 67279a6
@isaacs isaacs authored
Showing with 2,081 additions and 68 deletions.
  1. +1,629 −68 fs.js
  2. +452 −0 zlib.js
View
1,697 fs.js
@@ -1,96 +1,1481 @@
-'use strict';
+// 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.
-module.exports = FSReadable;
+// 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 = require('./lib/_stream_readable.js');
+var Writable = require('./lib/_stream_writable.js');
+
+var kMinPoolSpace = 128;
+var kPoolSize = 40 * 1024;
+
+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.message = err.message;
+ 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, encoding_) {
+ var encoding = typeof(encoding_) === 'string' ? encoding_ : null;
+ var callback = maybeCallback(arguments[arguments.length - 1]);
+
+ 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;
+
+ fs.open(path, constants.O_RDONLY, 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, encoding) {
+ assertEncoding(encoding);
+
+ var fd = fs.openSync(path, constants.O_RDONLY, 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.sendfile = function(outFd, inFd, inOffset, length, callback) {
+ binding.sendfile(outFd, inFd, inOffset, length, makeCallback(callback));
+};
+
+fs.sendfileSync = function(outFd, inFd, inOffset, length) {
+ return binding.sendfile(outFd, inFd, inOffset, length);
+};
+
+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, encoding_, callback) {
+ var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8');
+ assertEncoding(encoding);
+
+ callback = maybeCallback(arguments[arguments.length - 1]);
+ fs.open(path, 'w', 438 /*=0666*/, function(openErr, fd) {
+ if (openErr) {
+ if (callback) callback(openErr);
+ } else {
+ var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data,
+ encoding);
+ writeAll(fd, buffer, 0, buffer.length, 0, callback);
+ }
+ });
+};
+
+fs.writeFileSync = function(path, data, encoding) {
+ assertEncoding(encoding);
+
+ var fd = fs.openSync(path, 'w');
+ if (!Buffer.isBuffer(data)) {
+ data = new Buffer('' + data, encoding || 'utf8');
+ }
+ var written = 0;
+ var length = data.length;
+ try {
+ while (written < length) {
+ written += fs.writeSync(fd, data, written, length - written, written);
+ }
+ } finally {
+ fs.closeSync(fd);
+ }
+};
+
+fs.appendFile = function(path, data, encoding_, callback) {
+ var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8');
+ assertEncoding(encoding);
+
+ callback = maybeCallback(arguments[arguments.length - 1]);
+
+ fs.open(path, 'a', 438 /*=0666*/, function(err, fd) {
+ if (err) return callback(err);
+ var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data, encoding);
+ writeAll(fd, buffer, 0, buffer.length, null, callback);
+ });
+};
+
+fs.appendFileSync = function(path, data, encoding) {
+ assertEncoding(encoding);
+
+ var fd = fs.openSync(path, 'a');
+ if (!Buffer.isBuffer(data)) {
+ data = new Buffer('' + data, encoding || 'utf8');
+ }
+ var written = 0;
+ var position = null;
+ var length = data.length;
+
+ try {
+ while (written < length) {
+ written += fs.writeSync(fd, data, written, length - written, position);
+ position += written; // XXX not safe with multiple concurrent writers?
+ }
+ } finally {
+ fs.closeSync(fd);
+ }
+};
+
+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(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(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 (stat.listeners('change').length === 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();
+ }
+};
-// This uses the existing bindings in Node's FS module to
-// implement a read-method style readable stream.
-var Readable = require('./readable.js');
-var util = require('util');
-var fs = require('fs');
-var StringDecoder = require('string_decoder').StringDecoder;
-var assert = require('assert');
-// a very basic memory pool. this optimization helps revent lots
-// of allocations when there are many fs readable streams happening
-// concurrently.
var pool;
-var minPoolSpace = 128;
-var poolSize = 40 * 1024;
+
function allocNewPool() {
- pool = new Buffer(poolSize);
+ pool = new Buffer(kPoolSize);
pool.used = 0;
}
-util.inherits(FSReadable, Readable);
-function FSReadable(path, options) {
- if (!options)
- options = {};
- Readable.call(this, options);
+fs.createReadStream = function(path, options) {
+ return new ReadStream(path, options);
+};
- var state = this._readableState;
- this.path = path;
- this.flags = options.flags || 'r';
- this.mode = options.mode || 438; //=0666
- this.fd = options.fd || null;
+util.inherits(ReadStream, Readable);
+fs.ReadStream = ReadStream;
- // a little bit bigger buffer and watermark by default
- state.bufferSize = options.bufferSize || 64 * 1024;
- state.lowWaterMark = options.lowWaterMark || 16 * 1024;
+function ReadStream(path, options) {
+ if (!(this instanceof ReadStream))
+ return new ReadStream(path, options);
- if (this.encoding) {
- this._decoder = new StringDecoder(this.encoding);
- }
+ // a little bit bigger buffer and water marks by default
+ options = util._extend({
+ bufferSize: 64 * 1024,
+ lowWaterMark: 16 * 1024,
+ highWaterMark: 64 * 1024
+ }, options || {});
+
+ Readable.call(this, options);
- var typeofStart = typeof this.start;
- if (typeofStart !== 'undefined') {
- if (typeofStart !== 'number')
- throw new TypeError('start must be a Number');
+ 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('start') ? options.end : undefined;
+ this.pos = undefined;
- var typeofEnd = typeof this.end;
- if (typeofEnd === '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 (typeofEnd !== 'number')
- throw new TypeError('end must be a Number');
+ } 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() {
+ this.destroy();
+ });
}
-FSReadable.prototype.open = function() {
+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) {
- this.destroy();
- this.emit('error', er);
+ self.destroy();
+ self.emit('error', er);
return;
}
- this.fd = fd;
- this.emit('open', fd);
- }.bind(this));
+ self.fd = fd;
+ self.emit('open', fd);
+ // start the flow of data.
+ self.read();
+ });
};
-FSReadable.prototype._read = function(n, cb) {
- if (this.fd === null) {
- this.once('open', this._read.bind(this, n, cb));
- return;
- }
+ReadStream.prototype._read = function(n, cb) {
+ if (typeof this.fd !== 'number')
+ return this.once('open', function() {
+ this._read(n, cb);
+ });
if (this.destroyed)
return;
- if (!pool || pool.length - pool.used < minPoolSpace) {
+ if (!pool || pool.length - pool.used < kMinPoolSpace) {
// discard the old pool. Can't add to the free list because
// users might have refernces to slices on it.
pool = null;
allocNewPool();
}
+ // 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;
@@ -98,17 +1483,23 @@ FSReadable.prototype._read = function(n, cb) {
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 cb();
- if (toRead <= 0) {
- this.emit('readable');
- return;
- }
+ // the actual read.
+ var self = this;
+ fs.read(this.fd, pool, pool.used, toRead, this.pos, onread);
- fs.read(this.fd, pool, pool.used, toRead, this.pos, onread.bind(this));
+ // 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) {
- this.destroy();
+ self.destroy();
return cb(er);
}
@@ -120,25 +1511,195 @@ FSReadable.prototype._read = function(n, cb) {
}
};
-FSReadable.prototype.close = function(cb) {
+
+ReadStream.prototype.destroy = function() {
+ if (this.destroyed)
+ return;
+ this.destroyed = true;
+ if ('number' === typeof this.fd)
+ this.close();
+};
+
+
+ReadStream.prototype.close = function(cb) {
if (cb)
this.once('close', cb);
- if (this.closed || this.fd === null) {
- if (this.fd === null)
- this.once('open', this.destroy);
+ if (this.closed || 'number' !== typeof this.fd) {
+ if ('number' !== typeof this.fd)
+ this.once('open', close);
return process.nextTick(this.emit.bind(this, 'close'));
}
this.closed = true;
+ var self = this;
+ close();
+
+ function close() {
+ fs.close(self.fd, function(er) {
+ if (er)
+ self.emit('error', er);
+ else
+ self.emit('close');
+ });
+ }
+};
+
+
+
+
+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);
+
+ // a little bit bigger buffer and water marks by default
+ options = util._extend({
+ bufferSize: 64 * 1024,
+ lowWaterMark: 16 * 1024,
+ highWaterMark: 64 * 1024
+ }, options || {});
+
+ Writable.call(this, options);
+
+ this.path = path;
+ this.fd = null;
- fs.close(this.fd, function(er) {
- if (er)
+ 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);
- else
- this.emit('close');
+ return;
+ }
+
+ this.fd = fd;
+ this.emit('open', fd);
}.bind(this));
};
-FSReadable.prototype.destroy = function() {
- this.destroyed = true;
- fs.close(this.fd, function() {});
+
+WriteStream.prototype._write = function(data, cb) {
+ if (!Buffer.isBuffer(data))
+ return this.emit('error', new Error('Invalid data'));
+
+ if (typeof this.fd !== 'number')
+ return this.once('open', this._write.bind(this, data, cb));
+
+ fs.write(this.fd, data, 0, data.length, this.pos, function(er, bytes) {
+ if (er) {
+ this.destroy();
+ return cb(er);
+ }
+ this.bytesWritten += bytes;
+ cb();
+ }.bind(this));
+
+ 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;
View
452 zlib.js
@@ -0,0 +1,452 @@
+// 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.
+
+var Transform = require('./lib/_stream_transform.js');
+
+var binding = process.binding('zlib');
+var util = require('util');
+var assert = require('assert').ok;
+
+// zlib doesn't provide these, so kludge them in following the same
+// const naming scheme zlib uses.
+binding.Z_MIN_WINDOWBITS = 8;
+binding.Z_MAX_WINDOWBITS = 15;
+binding.Z_DEFAULT_WINDOWBITS = 15;
+
+// fewer than 64 bytes per chunk is stupid.
+// technically it could work with as few as 8, but even 64 bytes
+// is absurdly low. Usually a MB or more is best.
+binding.Z_MIN_CHUNK = 64;
+binding.Z_MAX_CHUNK = Infinity;
+binding.Z_DEFAULT_CHUNK = (16 * 1024);
+
+binding.Z_MIN_MEMLEVEL = 1;
+binding.Z_MAX_MEMLEVEL = 9;
+binding.Z_DEFAULT_MEMLEVEL = 8;
+
+binding.Z_MIN_LEVEL = -1;
+binding.Z_MAX_LEVEL = 9;
+binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION;
+
+// expose all the zlib constants
+Object.keys(binding).forEach(function(k) {
+ if (k.match(/^Z/)) exports[k] = binding[k];
+});
+
+// translation table for return codes.
+exports.codes = {
+ Z_OK: binding.Z_OK,
+ Z_STREAM_END: binding.Z_STREAM_END,
+ Z_NEED_DICT: binding.Z_NEED_DICT,
+ Z_ERRNO: binding.Z_ERRNO,
+ Z_STREAM_ERROR: binding.Z_STREAM_ERROR,
+ Z_DATA_ERROR: binding.Z_DATA_ERROR,
+ Z_MEM_ERROR: binding.Z_MEM_ERROR,
+ Z_BUF_ERROR: binding.Z_BUF_ERROR,
+ Z_VERSION_ERROR: binding.Z_VERSION_ERROR
+};
+
+Object.keys(exports.codes).forEach(function(k) {
+ exports.codes[exports.codes[k]] = k;
+});
+
+exports.Deflate = Deflate;
+exports.Inflate = Inflate;
+exports.Gzip = Gzip;
+exports.Gunzip = Gunzip;
+exports.DeflateRaw = DeflateRaw;
+exports.InflateRaw = InflateRaw;
+exports.Unzip = Unzip;
+
+exports.createDeflate = function(o) {
+ return new Deflate(o);
+};
+
+exports.createInflate = function(o) {
+ return new Inflate(o);
+};
+
+exports.createDeflateRaw = function(o) {
+ return new DeflateRaw(o);
+};
+
+exports.createInflateRaw = function(o) {
+ return new InflateRaw(o);
+};
+
+exports.createGzip = function(o) {
+ return new Gzip(o);
+};
+
+exports.createGunzip = function(o) {
+ return new Gunzip(o);
+};
+
+exports.createUnzip = function(o) {
+ return new Unzip(o);
+};
+
+
+// Convenience methods.
+// compress/decompress a string or buffer in one step.
+exports.deflate = function(buffer, callback) {
+ zlibBuffer(new Deflate(), buffer, callback);
+};
+
+exports.gzip = function(buffer, callback) {
+ zlibBuffer(new Gzip(), buffer, callback);
+};
+
+exports.deflateRaw = function(buffer, callback) {
+ zlibBuffer(new DeflateRaw(), buffer, callback);
+};
+
+exports.unzip = function(buffer, callback) {
+ zlibBuffer(new Unzip(), buffer, callback);
+};
+
+exports.inflate = function(buffer, callback) {
+ zlibBuffer(new Inflate(), buffer, callback);
+};
+
+exports.gunzip = function(buffer, callback) {
+ zlibBuffer(new Gunzip(), buffer, callback);
+};
+
+exports.inflateRaw = function(buffer, callback) {
+ zlibBuffer(new InflateRaw(), buffer, callback);
+};
+
+function zlibBuffer(engine, buffer, callback) {
+ var buffers = [];
+ var nread = 0;
+
+ engine.on('error', onError);
+ engine.on('end', onEnd);
+
+ engine.end(buffer);
+ flow();
+
+ function flow() {
+ var chunk;
+ while (null !== (chunk = engine.read())) {
+ buffers.push(chunk);
+ nread += chunk.length;
+ }
+ engine.once('readable', flow);
+ }
+
+ function onError(err) {
+ engine.removeListener('end', onEnd);
+ engine.removeListener('readable', flow);
+ callback(err);
+ }
+
+ function onEnd() {
+ var buf = Buffer.concat(buffers, nread);
+ buffers = [];
+ callback(null, buf);
+ }
+}
+
+
+// generic zlib
+// minimal 2-byte header
+function Deflate(opts) {
+ if (!(this instanceof Deflate)) return new Deflate(opts);
+ Zlib.call(this, opts, binding.DEFLATE);
+}
+
+function Inflate(opts) {
+ if (!(this instanceof Inflate)) return new Inflate(opts);
+ Zlib.call(this, opts, binding.INFLATE);
+}
+
+
+
+// gzip - bigger header, same deflate compression
+function Gzip(opts) {
+ if (!(this instanceof Gzip)) return new Gzip(opts);
+ Zlib.call(this, opts, binding.GZIP);
+}
+
+function Gunzip(opts) {
+ if (!(this instanceof Gunzip)) return new Gunzip(opts);
+ Zlib.call(this, opts, binding.GUNZIP);
+}
+
+
+
+// raw - no header
+function DeflateRaw(opts) {
+ if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts);
+ Zlib.call(this, opts, binding.DEFLATERAW);
+}
+
+function InflateRaw(opts) {
+ if (!(this instanceof InflateRaw)) return new InflateRaw(opts);
+ Zlib.call(this, opts, binding.INFLATERAW);
+}
+
+
+// auto-detect header.
+function Unzip(opts) {
+ if (!(this instanceof Unzip)) return new Unzip(opts);
+ Zlib.call(this, opts, binding.UNZIP);
+}
+
+
+// the Zlib class they all inherit from
+// This thing manages the queue of requests, and returns
+// true or false if there is anything in the queue when
+// you call the .write() method.
+
+function Zlib(opts, mode) {
+ this._opts = opts = opts || {};
+ this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK;
+
+ Transform.call(this, opts);
+
+ // means a different thing there.
+ this._readableState.chunkSize = null;
+
+ if (opts.chunkSize) {
+ if (opts.chunkSize < exports.Z_MIN_CHUNK ||
+ opts.chunkSize > exports.Z_MAX_CHUNK) {
+ throw new Error('Invalid chunk size: ' + opts.chunkSize);
+ }
+ }
+
+ if (opts.windowBits) {
+ if (opts.windowBits < exports.Z_MIN_WINDOWBITS ||
+ opts.windowBits > exports.Z_MAX_WINDOWBITS) {
+ throw new Error('Invalid windowBits: ' + opts.windowBits);
+ }
+ }
+
+ if (opts.level) {
+ if (opts.level < exports.Z_MIN_LEVEL ||
+ opts.level > exports.Z_MAX_LEVEL) {
+ throw new Error('Invalid compression level: ' + opts.level);
+ }
+ }
+
+ if (opts.memLevel) {
+ if (opts.memLevel < exports.Z_MIN_MEMLEVEL ||
+ opts.memLevel > exports.Z_MAX_MEMLEVEL) {
+ throw new Error('Invalid memLevel: ' + opts.memLevel);
+ }
+ }
+
+ if (opts.strategy) {
+ if (opts.strategy != exports.Z_FILTERED &&
+ opts.strategy != exports.Z_HUFFMAN_ONLY &&
+ opts.strategy != exports.Z_RLE &&
+ opts.strategy != exports.Z_FIXED &&
+ opts.strategy != exports.Z_DEFAULT_STRATEGY) {
+ throw new Error('Invalid strategy: ' + opts.strategy);
+ }
+ }
+
+ if (opts.dictionary) {
+ if (!Buffer.isBuffer(opts.dictionary)) {
+ throw new Error('Invalid dictionary: it should be a Buffer instance');
+ }
+ }
+
+ this._binding = new binding.Zlib(mode);
+
+ var self = this;
+ this._hadError = false;
+ this._binding.onerror = function(message, errno) {
+ // there is no way to cleanly recover.
+ // continuing only obscures problems.
+ self._binding = null;
+ self._hadError = true;
+
+ var error = new Error(message);
+ error.errno = errno;
+ error.code = exports.codes[errno];
+ self.emit('error', error);
+ };
+
+ this._binding.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS,
+ opts.level || exports.Z_DEFAULT_COMPRESSION,
+ opts.memLevel || exports.Z_DEFAULT_MEMLEVEL,
+ opts.strategy || exports.Z_DEFAULT_STRATEGY,
+ opts.dictionary);
+
+ this._buffer = new Buffer(this._chunkSize);
+ this._offset = 0;
+ this._closed = false;
+
+ this.once('end', this.close);
+}
+
+util.inherits(Zlib, Transform);
+
+Zlib.prototype.reset = function reset() {
+ return this._binding.reset();
+};
+
+Zlib.prototype._flush = function(output, callback) {
+ var rs = this._readableState;
+ var self = this;
+ this._transform(null, output, function(er) {
+ if (er)
+ return callback(er);
+
+ // now a weird thing happens... it could be that you called flush
+ // but everything had already actually been consumed, but it wasn't
+ // enough to get over the Readable class's lowWaterMark.
+ // In that case, we emit 'readable' now to make sure it's consumed.
+ if (rs.length &&
+ rs.length < rs.lowWaterMark &&
+ !rs.ended &&
+ rs.needReadable)
+ self.emit('readable');
+
+ callback();
+ });
+};
+
+Zlib.prototype.flush = function(callback) {
+ var ws = this._writableState;
+ var ts = this._transformState;
+
+ if (ws.writing) {
+ ws.needDrain = true;
+ var self = this;
+ this.once('drain', function() {
+ self._flush(ts.output, callback);
+ });
+ return;
+ }
+
+ this._flush(ts.output, callback || function() {});
+};
+
+Zlib.prototype.close = function(callback) {
+ if (callback)
+ process.nextTick(callback);
+
+ if (this._closed)
+ return;
+
+ this._closed = true;
+
+ this._binding.close();
+
+ var self = this;
+ process.nextTick(function() {
+ self.emit('close');
+ });
+};
+
+Zlib.prototype._transform = function(chunk, output, cb) {
+ var flushFlag;
+ var ws = this._writableState;
+ var ending = ws.ending || ws.ended;
+ var last = ending && (!chunk || ws.length === chunk.length);
+
+ if (chunk !== null && !Buffer.isBuffer(chunk))
+ return cb(new Error('invalid input'));
+
+ // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag.
+ // If it's explicitly flushing at some other time, then we use
+ // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression
+ // goodness.
+ if (last)
+ flushFlag = binding.Z_FINISH;
+ else if (chunk === null)
+ flushFlag = binding.Z_FULL_FLUSH;
+ else
+ flushFlag = binding.Z_NO_FLUSH;
+
+ var availInBefore = chunk && chunk.length;
+ var availOutBefore = this._chunkSize - this._offset;
+ var inOff = 0;
+
+ var req = this._binding.write(flushFlag,
+ chunk, // in
+ inOff, // in_off
+ availInBefore, // in_len
+ this._buffer, // out
+ this._offset, //out_off
+ availOutBefore); // out_len
+
+ req.buffer = chunk;
+ req.callback = callback;
+
+ var self = this;
+ function callback(availInAfter, availOutAfter, buffer) {
+ if (self._hadError)
+ return;
+
+ var have = availOutBefore - availOutAfter;
+ assert(have >= 0, 'have should not go down');
+
+ if (have > 0) {
+ var out = self._buffer.slice(self._offset, self._offset + have);
+ self._offset += have;
+ // serve some output to the consumer.
+ output(out);
+ }
+
+ // exhausted the output buffer, or used all the input create a new one.
+ if (availOutAfter === 0 || self._offset >= self._chunkSize) {
+ availOutBefore = self._chunkSize;
+ self._offset = 0;
+ self._buffer = new Buffer(self._chunkSize);
+ }
+
+ if (availOutAfter === 0) {
+ // Not actually done. Need to reprocess.
+ // Also, update the availInBefore to the availInAfter value,
+ // so that if we have to hit it a third (fourth, etc.) time,
+ // it'll have the correct byte counts.
+ inOff += (availInBefore - availInAfter);
+ availInBefore = availInAfter;
+
+ var newReq = self._binding.write(flushFlag,
+ chunk,
+ inOff,
+ availInBefore,
+ self._buffer,
+ self._offset,
+ self._chunkSize);
+ newReq.callback = callback; // this same function
+ newReq.buffer = chunk;
+ return;
+ }
+
+ // finished with the chunk.
+ cb();
+ }
+};
+
+util.inherits(Deflate, Zlib);
+util.inherits(Inflate, Zlib);
+util.inherits(Gzip, Zlib);
+util.inherits(Gunzip, Zlib);
+util.inherits(DeflateRaw, Zlib);
+util.inherits(InflateRaw, Zlib);
+util.inherits(Unzip, Zlib);
Please sign in to comment.
Something went wrong with that request. Please try again.