123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- /* wrench.js
- *
- * A collection of various utility functions I've found myself in need of
- * for use with Node.js (http://nodejs.org/). This includes things like:
- *
- * - Recursively deleting directories in Node.js (Sync, not Async)
- * - Recursively copying directories in Node.js (Sync, not Async)
- * - Recursively chmoding a directory structure from Node.js (Sync, not Async)
- * - Other things that I'll add here as time goes on. Shhhh...
- *
- * ~ Ryan McGrath (ryan [at] venodesigns.net)
- */
- var fs = require("fs"),
- _path = require("path"),
- isWindows = !!process.platform.match(/^win/);
- /* wrench.readdirSyncRecursive("directory_path");
- *
- * Recursively dives through directories and read the contents of all the
- * children directories.
- */
- exports.readdirSyncRecursive = function(baseDir) {
- baseDir = baseDir.replace(/\/$/, '');
- var readdirSyncRecursive = function(baseDir) {
- var files = [],
- curFiles,
- nextDirs,
- isDir = function(fname){
- return fs.existsSync(_path.join(baseDir, fname)) ? fs.statSync( _path.join(baseDir, fname) ).isDirectory() : false;
- },
- prependBaseDir = function(fname){
- return _path.join(baseDir, fname);
- };
- curFiles = fs.readdirSync(baseDir);
- nextDirs = curFiles.filter(isDir);
- curFiles = curFiles.map(prependBaseDir);
- files = files.concat( curFiles );
- while (nextDirs.length) {
- files = files.concat( readdirSyncRecursive( _path.join(baseDir, nextDirs.shift()) ) );
- }
- return files;
- };
- // convert absolute paths to relative
- var fileList = readdirSyncRecursive(baseDir).map(function(val){
- return _path.relative(baseDir, val);
- });
- return fileList;
- };
- /* wrench.readdirRecursive("directory_path", function(error, files) {});
- *
- * Recursively dives through directories and read the contents of all the
- * children directories.
- *
- * Asynchronous, so returns results/error in callback.
- * Callback receives the of files in currently recursed directory.
- * When no more directories are left, callback is called with null for all arguments.
- *
- */
- exports.readdirRecursive = function(baseDir, fn) {
- baseDir = baseDir.replace(/\/$/, '');
- var waitCount = 0;
- function readdirRecursive(curDir) {
- var prependcurDir = function(fname){
- return _path.join(curDir, fname);
- };
- waitCount++;
- fs.readdir(curDir, function(e, curFiles) {
- if (e) {
- fn(e);
- return;
- }
- waitCount--;
- curFiles = curFiles.map(prependcurDir);
- curFiles.forEach(function(it) {
- waitCount++;
- fs.stat(it, function(e, stat) {
- waitCount--;
- if (e) {
- fn(e);
- } else {
- if (stat.isDirectory()) {
- readdirRecursive(it);
- }
- }
- if (waitCount == 0) {
- fn(null, null);
- }
- });
- });
- fn(null, curFiles.map(function(val) {
- // convert absolute paths to relative
- return _path.relative(baseDir, val);
- }));
- if (waitCount == 0) {
- fn(null, null);
- }
- });
- };
- readdirRecursive(baseDir);
- };
- /* wrench.rmdirSyncRecursive("directory_path", failSilent);
- *
- * Recursively dives through directories and obliterates everything about it. This is a
- * Sync-function, which blocks things until it's done. No idea why anybody would want an
- * Asynchronous version. :\
- */
- exports.rmdirSyncRecursive = function(path, failSilent) {
- var files;
- try {
- files = fs.readdirSync(path);
- } catch (err) {
- if(failSilent) return;
- throw new Error(err.message);
- }
- /* Loop through and delete everything in the sub-tree after checking it */
- for(var i = 0; i < files.length; i++) {
- var file = _path.join(path, files[i]);
- var currFile = fs.lstatSync(file);
- if(currFile.isDirectory()) {
- // Recursive function back to the beginning
- exports.rmdirSyncRecursive(file);
- } else if(currFile.isSymbolicLink()) {
- // Unlink symlinks
- if (isWindows) {
- fs.chmodSync(file, 666) // Windows needs this unless joyent/node#3006 is resolved..
- }
- fs.unlinkSync(file);
- } else {
- // Assume it's a file - perhaps a try/catch belongs here?
- if (isWindows) {
- fs.chmodSync(file, 666) // Windows needs this unless joyent/node#3006 is resolved..
- }
- fs.unlinkSync(file);
- }
- }
- /* Now that we know everything in the sub-tree has been deleted, we can delete the main
- directory. Huzzah for the shopkeep. */
- return fs.rmdirSync(path);
- };
- function isFileIncluded(opts, dir, filename) {
- function isMatch(filter) {
- if (typeof filter === 'function') {
- return filter(filename, dir) === true;
- }
- else {
- // Maintain backwards compatibility and use just the filename
- return filename.match(filter);
- }
- }
- if (opts.include || opts.exclude) {
- if (opts.exclude) {
- if (isMatch(opts.exclude)) {
- return false;
- }
- }
- if (opts.include) {
- if (isMatch(opts.include)) {
- return true;
- }
- else {
- return false;
- }
- }
- return true;
- }
- else if (opts.filter) {
- var filter = opts.filter;
- if (!opts.whitelist) {
- // if !opts.whitelist is false every file or directory
- // which does match opts.filter will be ignored
- return isMatch(filter) ? false : true;
- } else {
- // if opts.whitelist is true every file or directory
- // which doesn't match opts.filter will be ignored
- return !isMatch(filter) ? false : true;
- }
- }
- return true;
- }
- /* wrench.copyDirSyncRecursive("directory_to_copy", "new_directory_location", opts);
- *
- * Recursively dives through a directory and moves all its files to a new location. This is a
- * Synchronous function, which blocks things until it's done. If you need/want to do this in
- * an Asynchronous manner, look at wrench.copyDirRecursively() below. Specify forceDelete to force directory overwrite.
- *
- * Note: Directories should be passed to this function without a trailing slash.
- */
- exports.copyDirSyncRecursive = function(sourceDir, newDirLocation, opts) {
- opts = opts || {};
- try {
- if(fs.statSync(newDirLocation).isDirectory()) {
- if(opts.forceDelete) {
- exports.rmdirSyncRecursive(newDirLocation);
- } else if(!opts.preserveFiles) {
- return new Error('You are trying to copy a directory onto a directory that already exists. Specify forceDelete or preserveFiles in the opts argument to specify desired behavior');
- }
- }
- } catch(e) { }
- /* Create the directory where all our junk is moving to; read the mode of the source directory and mirror it */
- var checkDir = fs.statSync(sourceDir);
- try {
- fs.mkdirSync(newDirLocation, checkDir.mode);
- } catch (e) {
- //if the directory already exists, that's okay
- if (e.code !== 'EEXIST') throw e;
- }
- var files = fs.readdirSync(sourceDir);
- var hasFilter = opts.filter || opts.include || opts.exclude;
- var preserveFiles = opts.preserveFiles === true;
- var preserveTimestamps = opts.preserveTimestamps === true;
- for(var i = 0; i < files.length; i++) {
- // ignores all files or directories which match the RegExp in opts.filter
- if(typeof opts !== 'undefined') {
- if (hasFilter) {
- if (!isFileIncluded(opts, sourceDir, files[i])) {
- continue;
- }
- }
-
- if (opts.excludeHiddenUnix && /^\./.test(files[i])) continue;
- }
- var currFile = fs.lstatSync(_path.join(sourceDir, files[i]));
- var fCopyFile = function(srcFile, destFile) {
- if(typeof opts !== 'undefined' && opts.preserveFiles && fs.existsSync(destFile)) return;
- var contents = fs.readFileSync(srcFile);
- fs.writeFileSync(destFile, contents);
- var stat = fs.lstatSync(srcFile);
- fs.chmodSync(destFile, stat.mode);
- if (preserveTimestamps) {
- fs.utimesSync(destFile, stat.atime, stat.mtime)
- }
- };
- if(currFile.isDirectory()) {
- /* recursion this thing right on back. */
- exports.copyDirSyncRecursive(_path.join(sourceDir, files[i]), _path.join(newDirLocation, files[i]), opts);
- } else if(currFile.isSymbolicLink()) {
- var symlinkFull = fs.readlinkSync(_path.join(sourceDir, files[i]));
- symlinkFull = _path.resolve(fs.realpathSync(sourceDir), symlinkFull);
- if (typeof opts !== 'undefined' && !opts.inflateSymlinks) {
- fs.symlinkSync(symlinkFull, _path.join(newDirLocation, files[i]));
- continue;
- }
- var tmpCurrFile = fs.lstatSync(symlinkFull);
- if (tmpCurrFile.isDirectory()) {
- exports.copyDirSyncRecursive(symlinkFull, _path.join(newDirLocation, files[i]), opts);
- } else {
- /* At this point, we've hit a file actually worth copying... so copy it on over. */
- fCopyFile(symlinkFull, _path.join(newDirLocation, files[i]));
- }
- } else {
- /* At this point, we've hit a file actually worth copying... so copy it on over. */
- fCopyFile(_path.join(sourceDir, files[i]), _path.join(newDirLocation, files[i]));
- }
- }
- };
- /* wrench.chmodSyncRecursive("directory", filemode);
- *
- * Recursively dives through a directory and chmods everything to the desired mode. This is a
- * Synchronous function, which blocks things until it's done.
- *
- * Note: Directories should be passed to this function without a trailing slash.
- */
- exports.chmodSyncRecursive = function(sourceDir, filemode) {
- var files = fs.readdirSync(sourceDir);
- for(var i = 0; i < files.length; i++) {
- var currFile = fs.lstatSync(_path.join(sourceDir, files[i]));
- if(currFile.isDirectory()) {
- /* ...and recursion this thing right on back. */
- exports.chmodSyncRecursive(_path.join(sourceDir, files[i]), filemode);
- } else {
- /* At this point, we've hit a file actually worth copying... so copy it on over. */
- fs.chmod(_path.join(sourceDir, files[i]), filemode);
- }
- }
- /* Finally, chmod the parent directory */
- fs.chmod(sourceDir, filemode);
- };
- /* wrench.chownSyncRecursive("directory", uid, gid);
- *
- * Recursively dives through a directory and chowns everything to the desired user and group. This is a
- * Synchronous function, which blocks things until it's done.
- *
- * Note: Directories should be passed to this function without a trailing slash.
- */
- exports.chownSyncRecursive = function(sourceDir, uid, gid) {
- var files = fs.readdirSync(sourceDir);
- for(var i = 0; i < files.length; i++) {
- var currFile = fs.lstatSync(_path.join(sourceDir, files[i]));
- if(currFile.isDirectory()) {
- /* ...and recursion this thing right on back. */
- exports.chownSyncRecursive(_path.join(sourceDir, files[i]), uid, gid);
- } else {
- /* At this point, we've hit a file actually worth chowning... so own it. */
- fs.chownSync(_path.join(sourceDir, files[i]), uid, gid);
- }
- }
- /* Finally, chown the parent directory */
- fs.chownSync(sourceDir, uid, gid);
- };
- /* wrench.rmdirRecursive("directory_path", callback);
- *
- * Recursively dives through directories and obliterates everything about it.
- */
- exports.rmdirRecursive = function rmdirRecursive(dir, failSilent, clbk){
- if(clbk === null || typeof clbk == 'undefined')
- clbk = function(err) {};
- fs.readdir(dir, function(err, files) {
- if(err && typeof failSilent === 'boolean' && !failSilent)
- return clbk(err);
- if(typeof failSilent === 'function')
- clbk = failSilent;
-
- (function rmFile(err){
- if (err) return clbk(err);
- var filename = files.shift();
- if (filename === null || typeof filename == 'undefined')
- return fs.rmdir(dir, clbk);
- var file = dir+'/'+filename;
- fs.lstat(file, function(err, stat){
- if (err) return clbk(err);
- if (stat.isDirectory())
- rmdirRecursive(file, rmFile);
- else
- fs.unlink(file, rmFile);
- });
- })();
- });
- };
- /* wrench.copyDirRecursive("directory_to_copy", "new_location", {forceDelete: bool}, callback);
- *
- * Recursively dives through a directory and moves all its files to a new
- * location. Specify forceDelete to force directory overwrite.
- *
- * Note: Directories should be passed to this function without a trailing slash.
- */
- exports.copyDirRecursive = function copyDirRecursive(srcDir, newDir, opts, clbk) {
- var originalArguments = Array.prototype.slice.apply(arguments);
- srcDir = _path.normalize(srcDir);
- newDir = _path.normalize(newDir);
- fs.stat(newDir, function(err, newDirStat) {
- if(!err) {
- if(typeof opts !== 'undefined' && typeof opts !== 'function' && opts.forceDelete)
- return exports.rmdirRecursive(newDir, function(err) {
- copyDirRecursive.apply(this, originalArguments);
- });
- else if(typeof opts !== 'undefined' && typeof opts !== 'function' && !opts.preserveFiles) {
- return clbk(new Error('You are trying to copy a directory onto a directory that already exists. Specify forceDelete or preserveFiles in the opts argument to specify desired behavior'));
- }
- }
- if(typeof opts === 'function')
- clbk = opts;
- fs.stat(srcDir, function(err, srcDirStat){
- if (err) return clbk(err);
- fs.mkdir(newDir, srcDirStat.mode, function(err){
- if (err) return clbk(err);
- fs.readdir(srcDir, function(err, files){
- if (err) return clbk(err);
- (function copyFiles(err){
- if (err) return clbk(err);
- var filename = files.shift();
- if (filename === null || typeof filename == 'undefined')
- return clbk(null);
- var file = srcDir+'/'+filename,
- newFile = newDir+'/'+filename;
- fs.stat(file, function(err, fileStat){
- if (err) return clbk(err);
- if (fileStat.isDirectory())
- copyDirRecursive(file, newFile, copyFiles, clbk);
- else if (fileStat.isSymbolicLink())
- fs.readlink(file, function(err, link){
- if (err) return clbk(err);
- fs.symlink(link, newFile, copyFiles);
- });
- else
- fs.readFile(file, function(err, data){
- if (err) return clbk(err);
- fs.writeFile(newFile, data, copyFiles);
- });
- });
- })();
- });
- });
- });
- });
- };
- var mkdirSyncRecursive = function(path, mode) {
- var self = this;
- path = _path.normalize(path)
- try {
- fs.mkdirSync(path, mode);
- } catch(err) {
- if(err.code == "ENOENT") {
- var slashIdx = path.lastIndexOf(_path.sep);
- if(slashIdx > 0) {
- var parentPath = path.substring(0, slashIdx);
- mkdirSyncRecursive(parentPath, mode);
- mkdirSyncRecursive(path, mode);
- } else {
- throw err;
- }
- } else if(err.code == "EEXIST") {
- return;
- } else {
- throw err;
- }
- }
- };
- exports.mkdirSyncRecursive = mkdirSyncRecursive;
- exports.LineReader = function(filename, bufferSize) {
- this.bufferSize = bufferSize || 8192;
- this.buffer = "";
- this.fd = fs.openSync(filename, "r");
- this.currentPosition = 0;
- };
- exports.LineReader.prototype = {
- close: function() {
- return fs.closeSync(this.fd);
- },
- getBufferAndSetCurrentPosition: function(position) {
- var res = fs.readSync(this.fd, this.bufferSize, position, "ascii");
- this.buffer += res[0];
- if(res[1] === 0) {
- this.currentPosition = -1;
- } else {
- this.currentPosition = position + res[1];
- }
- return this.currentPosition;
- },
- hasNextLine: function() {
- while(this.buffer.indexOf('\n') === -1) {
- this.getBufferAndSetCurrentPosition(this.currentPosition);
- if(this.currentPosition === -1) return false;
- }
- if(this.buffer.indexOf("\n") > -1 || this.buffer.length !== 0) return true;
- return false;
- },
- getNextLine: function() {
- var lineEnd = this.buffer.indexOf("\n"),
- result = this.buffer.substring(0, lineEnd != -1 ? lineEnd : this.buffer.length);
- this.buffer = this.buffer.substring(result.length + 1, this.buffer.length);
- return result;
- }
- };
- // vim: et ts=4 sw=4
|