Skip to content
Browse files

Made sync version support symlinks.

- handles cyclic links.
- not recursive.
  • Loading branch information...
1 parent e7622b3 commit 5d0015d14dfd61b53000b1b6f515b75ed7cd4ff7 @smh smh committed
Showing with 80 additions and 31 deletions.
  1. +19 −9 README.markdown
  2. +47 −21 index.js
  3. +14 −1 test/symlinks.js
View
28 README.markdown
@@ -21,13 +21,17 @@ emitter style
````javascript
var finder = require('findit').find(__dirname);
-finder.on('directory', function (dir) {
+finder.on('directory', function (dir, stat) {
console.log(dir + '/');
});
-finder.on('file', function (file) {
+finder.on('file', function (file, stat) {
console.log(file);
});
+
+finder.on('link', function (link, stat) {
+ console.log(link);
+});
````
synchronous
@@ -41,30 +45,36 @@ var files = require('findit').sync(__dirname);
methods
=======
-find(basedir)
--------------
-find(basedir, cb)
+find(basedir, options, cb)
-----------------
Do an asynchronous recursive walk starting at `basedir`.
+
+Optionally supply an options object. Setting the property 'follow_symlinks'
+will follow symlinks.
+
Optionally supply a callback that will get the same arguments as the path event
documented below in "events".
If `basedir` is actually a non-directory regular file, findit emits a single
"file" event for it then emits "end".
-Findit uses `fs.stat()` so symlinks are traversed automatically. Findit won't
-traverse an inode that it has seen before so directories can have symlink cycles
-and findit won't blow up.
+Findit uses `fs.lstat()` so symlinks are not traversed automatically. To have it
+follow symlinks, supply the options argument with 'follow_symlinks' set to true.
+Findit won't traverse an inode that it has seen before so directories can have
+symlink cycles and findit won't blow up.
Returns an EventEmitter. See "events".
-sync(basedir, cb)
+sync(basedir, options, cb)
-----------------
Return an array of files and directories from a synchronous recursive walk
starting at `basedir`.
+Optionally supply an options object. Setting the property 'follow_symlinks'
+will follow symlinks.
+
An optional callback `cb` will get called with `cb(file, stat)` if specified.
events
View
68 index.js
@@ -3,15 +3,27 @@ var path = require('path');
var EventEmitter = require('events').EventEmitter;
var Seq = require('seq');
+function createInodeChecker() {
+ var inodes = {};
+ return function inodeSeen(inode) {
+ if (inodes[inode]) {
+ return true;
+ } else {
+ inodes[inode] = true;
+ return false;
+ }
+ }
+}
+
exports = module.exports = find;
exports.find = find;
function find (base, options, cb) {
cb = arguments[arguments.length - 1];
if (typeof(cb) !== 'function') {
- cb = undefined;
+ cb = undefined;
}
var em = new EventEmitter;
- var inodes = {};
+ var inodeSeen = createInodeChecker();
function finder (dir, f) {
Seq()
@@ -29,7 +41,7 @@ function find (base, options, cb) {
var stat = this.vars[file];
if (cb) cb(file, stat);
- if (inodes[stat.ino]) {
+ if (inodeSeen(stat.ino)) {
// already seen this inode, probably a recursive symlink
this(null);
}
@@ -43,6 +55,7 @@ function find (base, options, cb) {
if (exists) {
fs.readlink(file, function(err, resolvedPath) {
if (err) {
+ em.emit('error', err);
} else {
finder(path.resolve(path.dir(file), resolvedPath));
}
@@ -61,8 +74,6 @@ function find (base, options, cb) {
em.emit('file', file, stat);
this(null);
}
-
- inodes[stat.ino] = true;
}
})
.seq(f.bind({}, null))
@@ -92,25 +103,40 @@ function find (base, options, cb) {
return em;
};
-exports.findSync = function findSync (dir, cb) {
- var rootStat = fs.lstatSync(dir);
- if (!rootStat.isDirectory()) {
- if (cb) cb(dir, rootStat);
- return [dir];
+exports.findSync = function findSync(dir, options, callback) {
+ cb = arguments[arguments.length - 1];
+ if (typeof(cb) !== 'function') {
+ cb = undefined;
}
-
- return fs.readdirSync(dir).reduce(function (files, file) {
- var p = dir + '/' + file;
- var stat = fs.lstatSync(p);
- if (cb) cb(p, stat);
- files.push(p);
-
+ var inodeSeen = createInodeChecker();
+ var files = [];
+ var fileQueue = [];
+ var processFile = function processFile(file) {
+ var stat = fs.lstatSync(file);
+ if (inodeSeen(stat.ino)) {
+ return;
+ }
+ files.push(file);
+ cb && cb(file, stat)
if (stat.isDirectory()) {
- files.push.apply(files, findSync(p, cb));
+ fs.readdirSync(file).forEach(function(f) { fileQueue.push(path.join(file, f)); });
+ } else if (stat.isSymbolicLink()) {
+ if (options && options.follow_symlinks && path.existsSync(file)) {
+ fileQueue.push(fs.realpathSync(file));
+ }
}
-
- return files;
- }, []);
+ };
+ /* we don't include the starting directory unless it is a file */
+ var stat = fs.lstatSync(dir);
+ if (stat.isDirectory()) {
+ fs.readdirSync(dir).forEach(function(f) { fileQueue.push(path.join(dir, f)); });
+ } else {
+ fileQueue.push(dir);
+ }
+ while (fileQueue.length > 0) {
+ processFile(fileQueue.shift());
+ }
+ return files;
};
exports.find.sync = exports.findSync;
View
15 test/symlinks.js
@@ -54,7 +54,20 @@ exports.links = function() {
exports.follow_links = function() {
find_helper(__dirname + '/symlinks/dir1', { follow_symlinks: true }, function(data) {
assert.eql(['cyclic-link-to-dir1', 'dangling-symlink', 'link-to-dir2', 'link-to-file'], data.symlinks);
- assert.eql(['file', 'file1'], data.files);
+ assert.eql(['file', 'file1', 'file2'], data.files);
assert.eql(['dir1', 'dir2'], data.dirs);
});
};
+
+exports.links_sync = function() {
+ var files = findit.findSync(__dirname + '/symlinks/dir1', { follow_symlinks: false }).map(path.basename);
+ files.sort();
+ assert.eql(['dangling-symlink', 'file1', 'link-to-dir2', 'link-to-file'], files);
+};
+
+exports.follow_links_sync = function() {
+ var files = findit.findSync(__dirname + '/symlinks/dir1', { follow_symlinks: true }).map(path.basename);
+ files.sort();
+ assert.eql(['cyclic-link-to-dir1', 'dangling-symlink', 'dir1', 'dir2', 'file', 'file1', 'file2', 'link-to-dir2', 'link-to-file'], files);
+};
+

0 comments on commit 5d0015d

Please sign in to comment.
Something went wrong with that request. Please try again.