123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- /*!
- * ndir - lib/ndir.js
- * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
- * MIT Licensed
- */
- /**
- * Module dependencies.
- */
- var fs = require('fs');
- var path = require('path');
- var util = require('util');
- var EventEmitter = require('events').EventEmitter;
- fs.exists = fs.exists || path.exists;
- /**
- * dir Walker Class.
- *
- * @constructor
- * @param {String} root Root path.
- * @param {Function(dirpath, files)} [onDir] The `dir` event callback.
- * @param {Function} [onEnd] The `end` event callback.
- * @param {Function(err)} [onError] The `error` event callback.
- * @public
- */
- function Walk(root, onDir, onEnd, onError) {
- if (!(this instanceof Walk)) {
- return new Walk(root, onDir, onEnd, onError);
- }
- this.dirs = [path.resolve(root)];
- if (onDir) {
- this.on('dir', onDir);
- }
- if (onEnd) {
- this.on('end', onEnd);
- }
- onError && this.on('error', onError);
- var self = this;
- // let listen `files` Event first.
- process.nextTick(function () {
- self.next();
- });
- }
- util.inherits(Walk, EventEmitter);
- exports.Walk = Walk;
- /**
- * Walking dir base on `Event`.
- *
- * @param {String} dir Start walking path.
- * @param {Function(dir)} [onDir] When a dir walk through, `emit('dir', dirpath, files)`.
- * @param {Function} [onEnd] When a dir walk over, `emit('end')`.
- * @param {Function(err)} [onError] When stat a path error, `emit('error', err, path)`.
- * @return {Walk} dir walker instance.
- * @public
- */
- exports.walk = function walk(dir, onDir, onEnd, onError) {
- return new Walk(dir, onDir, onEnd, onError);
- };
- /**
- * Next move, if move to the end,
- * will `emit('end')` event.
- *
- * @private
- */
- Walk.prototype.next = function () {
- var dir = this.dirs.shift();
- if (!dir) {
- return this.emit('end');
- }
- this._dir(dir);
- };
- /**
- * @private
- */
- Walk.prototype._dir = function (dir) {
- var self = this;
- fs.readdir(dir, function (err, files) {
- if (err) {
- self.emit('error', err, dir);
- return self.next();
- }
- var infos = [];
- if (files.length === 0) {
- self.emit('dir', dir, infos);
- return self.next();
- }
- var counter = 0;
- files.forEach(function (file) {
- var p = path.join(dir, file);
- fs.lstat(p, function (err, stats) {
- counter++;
- if (err) {
- self.emit('error', err, p);
- } else {
- infos.push([p, stats]);
- if (stats.isDirectory()) {
- self.dirs.push(p);
- }
- }
- if (counter === files.length) {
- self.emit('dir', dir, infos);
- self.next();
- }
- });
- });
- });
- };
- /**
- * Copy file, auto create tofile dir if dir not exists.
- *
- * @param {String} fromfile, Source file path.
- * @param {String} tofile, Target file path.
- * @param {Function(err)} callback
- * @public
- */
- exports.copyfile = function copyfile(fromfile, tofile, callback) {
- fromfile = path.resolve(fromfile);
- tofile = path.resolve(tofile);
- if (fromfile === tofile) {
- var msg = 'cp: "' + fromfile + '" and "' + tofile + '" are identical (not copied).';
- return callback(new Error(msg));
- }
- exports.mkdir(path.dirname(tofile), function (err) {
- if (err) {
- return callback(err);
- }
- var ws = fs.createWriteStream(tofile);
- var rs = fs.createReadStream(fromfile);
- var onerr = function (err) {
- callback && callback(err);
- callback = null;
- };
- ws.once('error', onerr); // if file not open, these is only error event will be emit.
- rs.once('error', onerr);
- ws.on('close', function () {
- // after file open, error event could be fire close event before.
- callback && callback();
- callback = null;
- });
- rs.pipe(ws);
- });
- };
- /**
- * @private
- */
- function _mkdir(dir, mode, callback) {
- fs.exists(dir, function (exists) {
- if (exists) {
- return callback();
- }
- fs.mkdir(dir, mode, callback);
- });
- }
- /**
- * mkdir if dir not exists, equal mkdir -p /path/foo/bar
- *
- * @param {String} dir
- * @param {Number} [mode] file mode, default is 0777.
- * @param {Function(err)} callback
- * @public
- */
- exports.mkdir = function mkdir(dir, mode, callback) {
- if (typeof mode === 'function') {
- callback = mode;
- mode = 0777 & (~process.umask());
- }
- var parent = path.dirname(dir);
- fs.exists(parent, function (exists) {
- if (exists) {
- return _mkdir(dir, mode, callback);
- }
- exports.mkdir(parent, mode, function (err) {
- if (err) {
- return callback(err);
- }
- _mkdir(dir, mode, callback);
- });
- });
- };
- exports.mkdirp = exports.mkdir;
- /**
- * Read stream data line by line.
- *
- * @constructor
- * @param {String|ReadStream} file File path or data stream object.
- */
- function LineReader(file) {
- if (typeof file === 'string') {
- this.readstream = fs.createReadStream(file);
- } else {
- this.readstream = file;
- }
- this.remainBuffers = [];
- var self = this;
- this.readstream.on('data', function (data) {
- self.ondata(data);
- });
- this.readstream.on('error', function (err) {
- self.emit('error', err);
- });
- this.readstream.on('end', function () {
- self.emit('end');
- });
- }
- util.inherits(LineReader, EventEmitter);
- /**
- * `Stream` data event handler.
- *
- * @param {Buffer} data
- * @private
- */
- LineReader.prototype.ondata = function (data) {
- var i = 0;
- var found = false;
- for (var l = data.length; i < l; i++) {
- if (data[i] === 10) {
- found = true;
- break;
- }
- }
- if (!found) {
- this.remainBuffers.push(data);
- return;
- }
- var line = null;
- if (this.remainBuffers.length > 0) {
- var size = i;
- var j, jl = this.remainBuffers.length;
- for (j = 0; j < jl; j++) {
- size += this.remainBuffers[j].length;
- }
- line = new Buffer(size);
- var pos = 0;
- for (j = 0; j < jl; j++) {
- var buf = this.remainBuffers[j];
- buf.copy(line, pos);
- pos += buf.length;
- }
- // check if `\n` is the first char in `data`
- if (i > 0) {
- data.copy(line, pos, 0, i);
- }
- this.remainBuffers = [];
- } else {
- line = data.slice(0, i);
- }
- this.emit('line', line);
- this.ondata(data.slice(i + 1));
- };
- /**
- * Line data reader
- *
- * @example
- * ```
- * var ndir = require('ndir');
- * ndir.createLineReader('/tmp/access.log')
- * .on('line', function (line) {
- * console.log(line.toString());
- * })
- * .on('end', function () {
- * console.log('end');
- * })
- * .on('error', function (err) {
- * console.error(err);
- * });
- * ```
- *
- * @param {String|ReadStream} file, file path or a `ReadStream` object.
- */
- exports.createLineReader = function (file) {
- return new LineReader(file);
- };
|