166 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			166 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| (function () {
 | |
|   "use strict";
 | |
| 
 | |
|   var fs = require('fs'),
 | |
|     fstat = fs.lstat,
 | |
|     Futures = require('futures'),
 | |
|     EventEmitter = require('events').EventEmitter,
 | |
|     upath = require('path'),
 | |
|     // "FIFO" isn't easy to convert to camelCase and back reliably
 | |
|     isFnodeTypes = [
 | |
|       "isFile", "isDirectory",  "isBlockDevice",  "isCharacterDevice",  "isSymbolicLink", "isFIFO", "isSocket"
 | |
|     ],
 | |
|     fnodeTypes = [
 | |
|       "file",   "directory",    "blockDevice",    "characterDevice",    "symbolicLink",   "FIFO",   "socket"
 | |
|     ],
 | |
|     fnodeTypesPlural = [
 | |
|       "files",  "directories",  "blockDevices",   "characterDevices",   "symbolicLinks",  "FIFOs",  "sockets"
 | |
|     ];
 | |
| 
 | |
|   // Get the current number of listeners (which may change)
 | |
|   // Emit events to each listener
 | |
|   // Wait for all listeners to `next()` before continueing
 | |
|   // (in theory this may avoid disk thrashing)
 | |
|   function emitSingleEvents(emitter, path, stats, next) {
 | |
|     var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
 | |
| 
 | |
|     function nextWhenReady() {
 | |
|       num -= 1;
 | |
|       if (0 === num) { next(); }
 | |
|     }
 | |
| 
 | |
|     emitter.emit(stats.type, path, stats, nextWhenReady);
 | |
|     emitter.emit("node", path, stats, nextWhenReady);
 | |
|     nextWhenReady();
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // Since the risk for disk thrashing among anything
 | |
|   // other than files is relatively low, all types are
 | |
|   // emitted at once, but all must complete before advancing
 | |
|   function emitPluralEvents(emitter, path, nodes, next) {
 | |
|     var num = 1;
 | |
| 
 | |
|     function nextWhenReady() {
 | |
|       num -= 1;
 | |
|       if (0 === num) { next(); }
 | |
|     }
 | |
| 
 | |
|     fnodeTypesPlural.concat(["nodes", "errors"]).forEach(function (fnodeType) {
 | |
|       if (0 === nodes[fnodeType].length) { return; }
 | |
|       num += emitter.listeners(fnodeType).length;
 | |
|       emitter.emit(fnodeType, path, nodes[fnodeType], nextWhenReady);
 | |
|     });
 | |
|     nextWhenReady();
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // Determine each file node's type
 | |
|   // 
 | |
|   function sortFnodesByType(stats, fnodes) {
 | |
|     isFnodeTypes.forEach(function (isType, i) {
 | |
|       if (stats[isType]()) {
 | |
|         if (stats.type) { throw new Error("is_" + type + " and " + isType); }
 | |
|         stats.type = fnodeTypes[i];
 | |
|         fnodes[fnodeTypesPlural[i]].push(stats);
 | |
|         //console.log(isType, fnodeTypesPlural[i], stats.name);
 | |
|         // TODO throw to break;
 | |
|       }
 | |
|     });
 | |
|     /*
 | |
|     // Won't really ever happen
 | |
|     if (!stats.type) {
 | |
|       stats.error = new Error(upath.join(path, stats.name) + ' isAnUndefinedType');
 | |
|     }
 | |
|     */
 | |
|   }
 | |
| 
 | |
|   function create(path) {
 | |
|     var emitter = new EventEmitter(),
 | |
|       paths = [],
 | |
|       path;
 | |
| 
 | |
|     function next() {
 | |
|       // path could be local if dirHandler were anonymous
 | |
|       //console.log('LEN: '+ paths.length);
 | |
|       if (0 == paths.length) {
 | |
|         emitter.emit('end');
 | |
|         return;
 | |
|       }
 | |
|       path = paths.pop();
 | |
|       //console.log("POP: " + path);
 | |
|       fs.readdir(path, dirHandler);
 | |
|     }
 | |
| 
 | |
|     function nodesHandler(nodes, args) {
 | |
|       //console.log('USE: ' + path);
 | |
|       var statses = [];
 | |
| 
 | |
|       var nodeGroups = {};
 | |
|       fnodeTypesPlural.concat("nodes", "errors").forEach(function (fnodeTypePlural) {
 | |
|         nodeGroups[fnodeTypePlural] = [];
 | |
|       });
 | |
| 
 | |
|       args.forEach(function (arg, i) {
 | |
|         var file = nodes[i],
 | |
|           err = arg[0],
 | |
|           stats = arg[1];
 | |
| 
 | |
|         if (err) {
 | |
|           stats = { error: err, name: file };
 | |
|           emitter.emit('error', err, path, stats);
 | |
|         }
 | |
|         if (stats) {
 | |
|           stats.name = file;
 | |
|           sortFnodesByType(stats, nodeGroups);
 | |
|           emitter.emit('stat', path, stats);
 | |
|         }
 | |
|       });
 | |
|       emitter.emit('stats', path, statses);
 | |
|       nodeGroups['directories'].forEach(function (stat) {
 | |
|         paths.push(path + '/' + stat.name);
 | |
|         //console.log('PUSH: ' + path + '/' + stat.name);
 | |
|       });
 | |
|       /*
 | |
|       //console.log('USE: ' + path);
 | |
|       next();
 | |
|       */
 | |
|       emitPluralEvents(emitter, path, nodeGroups, next);
 | |
|     }
 | |
| 
 | |
|     function dirHandler(err, nodes) {
 | |
|       //console.log("HANDLE: " + path);
 | |
|       var join = Futures.join(),
 | |
|         i;
 | |
| 
 | |
|       if (err) {
 | |
|         emitter.emit('error', err, path);
 | |
|       }
 | |
|       if (!nodes || 0 == nodes.length) {
 | |
|         //console.log('EMPTY: ' + path);
 | |
|         return next();
 | |
|       }
 | |
|       // TODO don't duplicate efforts
 | |
|       emitter.emit('nodes', path, nodes);
 | |
| 
 | |
|       for (i = 0; i < nodes.length; i += 1) {
 | |
|         fstat(path + '/' + nodes[i], join.deliverer());
 | |
|       }
 | |
| 
 | |
|       join.when(function () {
 | |
|         var args = Array.prototype.slice.call(arguments);
 | |
|         nodesHandler(nodes, args);
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     //paths.push([path]);
 | |
|     paths.push(path);
 | |
| 
 | |
| 
 | |
|     next();
 | |
|     return emitter;
 | |
|   }
 | |
| 
 | |
|   module.exports = create;
 | |
| }());
 |