var path = require('path'); var fs = require('fs'); var common = require('./common'); var _cd = require('./cd'); var _pwd = require('./pwd'); //@ //@ ### ls([options,] [path, ...]) //@ ### ls([options,] path_array) //@ Available options: //@ //@ + `-R`: recursive //@ + `-A`: all files (include files beginning with `.`, except for `.` and `..`) //@ + `-d`: list directories themselves, not their contents //@ + `-l`: list objects representing each file, each with fields containing `ls //@ -l` output fields. See //@ [fs.Stats](https://nodejs.org/api/fs.html#fs_class_fs_stats) //@ for more info //@ //@ Examples: //@ //@ ```javascript //@ ls('projs/*.js'); //@ ls('-R', '/users/me', '/tmp'); //@ ls('-R', ['/users/me', '/tmp']); // same as above //@ ls('-l', 'file.txt'); // { name: 'file.txt', mode: 33188, nlink: 1, ...} //@ ``` //@ //@ Returns array of files in the given path, or in current directory if no path provided. function _ls(options, paths) { options = common.parseOptions(options, { 'R': 'recursive', 'A': 'all', 'a': 'all_deprecated', 'd': 'directory', 'l': 'long' }); if (options.all_deprecated) { // We won't support the -a option as it's hard to image why it's useful // (it includes '.' and '..' in addition to '.*' files) // For backwards compatibility we'll dump a deprecated message and proceed as before common.log('ls: Option -a is deprecated. Use -A instead'); options.all = true; } if (!paths) paths = ['.']; else if (typeof paths === 'object') paths = paths; // assume array else if (typeof paths === 'string') paths = [].slice.call(arguments, 1); var list = []; // Conditionally pushes file to list - returns true if pushed, false otherwise // (e.g. prevents hidden files to be included unless explicitly told so) function pushFile(file, query) { var name = file.name || file; // hidden file? if (path.basename(name)[0] === '.') { // not explicitly asking for hidden files? if (!options.all && !(path.basename(query)[0] === '.' && path.basename(query).length > 1)) return false; } if (common.platform === 'win') name = name.replace(/\\/g, '/'); if (file.name) { file.name = name; } else { file = name; } list.push(file); return true; } paths.forEach(function(p) { if (fs.existsSync(p)) { var stats = ls_stat(p); // Simple file? if (stats.isFile()) { if (options.long) { pushFile(stats, p); } else { pushFile(p, p); } return; // continue } // Simple dir? if (options.directory) { pushFile(p, p); return; } else if (stats.isDirectory()) { // Iterate over p contents fs.readdirSync(p).forEach(function(file) { var orig_file = file; if (options.long) file = ls_stat(path.join(p, file)); if (!pushFile(file, p)) return; // Recursive? if (options.recursive) { var oldDir = _pwd(); _cd('', p); if (fs.statSync(orig_file).isDirectory()) list = list.concat(_ls('-R'+(options.all?'A':''), orig_file+'/*')); _cd('', oldDir); } }); return; // continue } } // p does not exist - possible wildcard present var basename = path.basename(p); var dirname = path.dirname(p); // Wildcard present on an existing dir? (e.g. '/tmp/*.js') if (basename.search(/\*/) > -1 && fs.existsSync(dirname) && fs.statSync(dirname).isDirectory) { // Escape special regular expression chars var regexp = basename.replace(/(\^|\$|\(|\)|<|>|\[|\]|\{|\}|\.|\+|\?)/g, '\\$1'); // Translates wildcard into regex regexp = '^' + regexp.replace(/\*/g, '.*') + '$'; // Iterate over directory contents fs.readdirSync(dirname).forEach(function(file) { if (file.match(new RegExp(regexp))) { var file_path = path.join(dirname, file); file_path = options.long ? ls_stat(file_path) : file_path; if (file_path.name) file_path.name = path.normalize(file_path.name); else file_path = path.normalize(file_path); if (!pushFile(file_path, basename)) return; // Recursive? if (options.recursive) { var pp = dirname + '/' + file; if (fs.lstatSync(pp).isDirectory()) list = list.concat(_ls('-R'+(options.all?'A':''), pp+'/*')); } // recursive } // if file matches }); // forEach return; } common.error('no such file or directory: ' + p, true); }); return list; } module.exports = _ls; function ls_stat(path) { var stats = fs.statSync(path); // Note: this object will contain more information than .toString() returns stats.name = path; stats.toString = function() { // Return a string resembling unix's `ls -l` format return [this.mode, this.nlink, this.uid, this.gid, this.size, this.mtime, this.name].join(' '); }; return stats; }