http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/shell.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/shell.js b/node_modules/shelljs/shell.js new file mode 100644 index 0000000..7a4f4c8 --- /dev/null +++ b/node_modules/shelljs/shell.js @@ -0,0 +1,1901 @@ +// +// ShellJS +// Unix shell commands on top of Node's API +// +// Copyright (c) 2012 Artur Adib +// http://github.com/arturadib/shelljs +// + +var fs = require('fs'), + path = require('path'), + util = require('util'), + vm = require('vm'), + child = require('child_process'), + os = require('os'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +var config = { + silent: false, + fatal: false +}; + +var state = { + error: null, + currentCmd: 'shell.js', + tempDir: null + }, + platform = os.type().match(/^Win/) ? 'win' : 'unix'; + + +//@ +//@ All commands run synchronously, unless otherwise stated. +//@ + + +//@ +//@ ### cd('dir') +//@ Changes to directory `dir` for the duration of the script +function _cd(options, dir) { + if (!dir) + error('directory not specified'); + + if (!fs.existsSync(dir)) + error('no such file or directory: ' + dir); + + if (!fs.statSync(dir).isDirectory()) + error('not a directory: ' + dir); + + process.chdir(dir); +} +exports.cd = wrap('cd', _cd); + +//@ +//@ ### pwd() +//@ Returns the current directory. +function _pwd(options) { + var pwd = path.resolve(process.cwd()); + return ShellString(pwd); +} +exports.pwd = wrap('pwd', _pwd); + + +//@ +//@ ### ls([options ,] path [,path ...]) +//@ ### ls([options ,] path_array) +//@ Available options: +//@ +//@ + `-R`: recursive +//@ + `-A`: all files (include files beginning with `.`, except for `.` and `..`) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ ls('projs/*.js'); +//@ ls('-R', '/users/me', '/tmp'); +//@ ls('-R', ['/users/me', '/tmp']); // same as above +//@ ``` +//@ +//@ Returns array of files in the given path, or in current directory if no path provided. +function _ls(options, paths) { + options = parseOptions(options, { + 'R': 'recursive', + 'A': 'all', + 'a': 'all_deprecated' + }); + + 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 + 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) { + // hidden file? + if (path.basename(file)[0] === '.') { + // not explicitly asking for hidden files? + if (!options.all && !(path.basename(query)[0] === '.' && path.basename(query).length > 1)) + return false; + } + + if (platform === 'win') + file = file.replace(/\\/g, '/'); + + list.push(file); + return true; + } + + paths.forEach(function(p) { + if (fs.existsSync(p)) { + var stats = fs.statSync(p); + // Simple file? + if (stats.isFile()) { + pushFile(p, p); + return; // continue + } + + // Simple dir? + if (stats.isDirectory()) { + // Iterate over p contents + fs.readdirSync(p).forEach(function(file) { + if (!pushFile(file, p)) + return; + + // Recursive? + if (options.recursive) { + var oldDir = _pwd(); + _cd('', p); + if (fs.statSync(file).isDirectory()) + list = list.concat(_ls('-R'+(options.all?'A':''), 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))) { + if (!pushFile(path.normalize(dirname+'/'+file), 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; + } + + error('no such file or directory: ' + p, true); + }); + + return list; +} +exports.ls = wrap('ls', _ls); + + +//@ +//@ ### find(path [,path ...]) +//@ ### find(path_array) +//@ Examples: +//@ +//@ ```javascript +//@ find('src', 'lib'); +//@ find(['src', 'lib']); // same as above +//@ find('.').filter(function(file) { return file.match(/\.js$/); }); +//@ ``` +//@ +//@ Returns array of all files (however deep) in the given paths. +//@ +//@ The main difference from `ls('-R', path)` is that the resulting file names +//@ include the base directories, e.g. `lib/resources/file1` instead of just `file1`. +function _find(options, paths) { + if (!paths) + error('no path specified'); + else if (typeof paths === 'object') + paths = paths; // assume array + else if (typeof paths === 'string') + paths = [].slice.call(arguments, 1); + + var list = []; + + function pushFile(file) { + if (platform === 'win') + file = file.replace(/\\/g, '/'); + list.push(file); + } + + // why not simply do ls('-R', paths)? because the output wouldn't give the base dirs + // to get the base dir in the output, we need instead ls('-R', 'dir/*') for every directory + + paths.forEach(function(file) { + pushFile(file); + + if (fs.statSync(file).isDirectory()) { + _ls('-RA', file+'/*').forEach(function(subfile) { + pushFile(subfile); + }); + } + }); + + return list; +} +exports.find = wrap('find', _find); + + +//@ +//@ ### cp([options ,] source [,source ...], dest) +//@ ### cp([options ,] source_array, dest) +//@ Available options: +//@ +//@ + `-f`: force +//@ + `-r, -R`: recursive +//@ +//@ Examples: +//@ +//@ ```javascript +//@ cp('file1', 'dir1'); +//@ cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp'); +//@ cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above +//@ ``` +//@ +//@ Copies files. The wildcard `*` is accepted. +function _cp(options, sources, dest) { + options = parseOptions(options, { + 'f': 'force', + 'R': 'recursive', + 'r': 'recursive' + }); + + // Get sources, dest + if (arguments.length < 3) { + error('missing <source> and/or <dest>'); + } else if (arguments.length > 3) { + sources = [].slice.call(arguments, 1, arguments.length - 1); + dest = arguments[arguments.length - 1]; + } else if (typeof sources === 'string') { + sources = [sources]; + } else if ('length' in sources) { + sources = sources; // no-op for array + } else { + error('invalid arguments'); + } + + var exists = fs.existsSync(dest), + stats = exists && fs.statSync(dest); + + // Dest is not existing dir, but multiple sources given + if ((!exists || !stats.isDirectory()) && sources.length > 1) + error('dest is not a directory (too many sources)'); + + // Dest is an existing file, but no -f given + if (exists && stats.isFile() && !options.force) + error('dest file already exists: ' + dest); + + if (options.recursive) { + // Recursive allows the shortcut syntax "sourcedir/" for "sourcedir/*" + // (see Github issue #15) + sources.forEach(function(src, i) { + if (src[src.length - 1] === '/') + sources[i] += '*'; + }); + + // Create dest + try { + fs.mkdirSync(dest, parseInt('0777', 8)); + } catch (e) { + // like Unix's cp, keep going even if we can't create dest dir + } + } + + sources = expand(sources); + + sources.forEach(function(src) { + if (!fs.existsSync(src)) { + error('no such file or directory: '+src, true); + return; // skip file + } + + // If here, src exists + if (fs.statSync(src).isDirectory()) { + if (!options.recursive) { + // Non-Recursive + log(src + ' is a directory (not copied)'); + } else { + // Recursive + // 'cp /a/source dest' should create 'source' in 'dest' + var newDest = path.join(dest, path.basename(src)), + checkDir = fs.statSync(src); + try { + fs.mkdirSync(newDest, checkDir.mode); + } catch (e) { + //if the directory already exists, that's okay + if (e.code !== 'EEXIST') throw e; + } + + cpdirSyncRecursive(src, newDest, {force: options.force}); + } + return; // done with dir + } + + // If here, src is a file + + // When copying to '/path/dir': + // thisDest = '/path/dir/file1' + var thisDest = dest; + if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) + thisDest = path.normalize(dest + '/' + path.basename(src)); + + if (fs.existsSync(thisDest) && !options.force) { + error('dest file already exists: ' + thisDest, true); + return; // skip file + } + + copyFileSync(src, thisDest); + }); // forEach(src) +} +exports.cp = wrap('cp', _cp); + +//@ +//@ ### rm([options ,] file [, file ...]) +//@ ### rm([options ,] file_array) +//@ Available options: +//@ +//@ + `-f`: force +//@ + `-r, -R`: recursive +//@ +//@ Examples: +//@ +//@ ```javascript +//@ rm('-rf', '/tmp/*'); +//@ rm('some_file.txt', 'another_file.txt'); +//@ rm(['some_file.txt', 'another_file.txt']); // same as above +//@ ``` +//@ +//@ Removes files. The wildcard `*` is accepted. +function _rm(options, files) { + options = parseOptions(options, { + 'f': 'force', + 'r': 'recursive', + 'R': 'recursive' + }); + if (!files) + error('no paths given'); + + if (typeof files === 'string') + files = [].slice.call(arguments, 1); + // if it's array leave it as it is + + files = expand(files); + + files.forEach(function(file) { + if (!fs.existsSync(file)) { + // Path does not exist, no force flag given + if (!options.force) + error('no such file or directory: '+file, true); + + return; // skip file + } + + // If here, path exists + + var stats = fs.statSync(file); + // Remove simple file + if (stats.isFile()) { + + // Do not check for file writing permissions + if (options.force) { + _unlinkSync(file); + return; + } + + if (isWriteable(file)) + _unlinkSync(file); + else + error('permission denied: '+file, true); + + return; + } // simple file + + // Path is an existing directory, but no -r flag given + if (stats.isDirectory() && !options.recursive) { + error('path is a directory', true); + return; // skip path + } + + // Recursively remove existing directory + if (stats.isDirectory() && options.recursive) { + rmdirSyncRecursive(file, options.force); + } + }); // forEach(file) +} // rm +exports.rm = wrap('rm', _rm); + +//@ +//@ ### mv(source [, source ...], dest') +//@ ### mv(source_array, dest') +//@ Available options: +//@ +//@ + `f`: force +//@ +//@ Examples: +//@ +//@ ```javascript +//@ mv('-f', 'file', 'dir/'); +//@ mv('file1', 'file2', 'dir/'); +//@ mv(['file1', 'file2'], 'dir/'); // same as above +//@ ``` +//@ +//@ Moves files. The wildcard `*` is accepted. +function _mv(options, sources, dest) { + options = parseOptions(options, { + 'f': 'force' + }); + + // Get sources, dest + if (arguments.length < 3) { + error('missing <source> and/or <dest>'); + } else if (arguments.length > 3) { + sources = [].slice.call(arguments, 1, arguments.length - 1); + dest = arguments[arguments.length - 1]; + } else if (typeof sources === 'string') { + sources = [sources]; + } else if ('length' in sources) { + sources = sources; // no-op for array + } else { + error('invalid arguments'); + } + + sources = expand(sources); + + var exists = fs.existsSync(dest), + stats = exists && fs.statSync(dest); + + // Dest is not existing dir, but multiple sources given + if ((!exists || !stats.isDirectory()) && sources.length > 1) + error('dest is not a directory (too many sources)'); + + // Dest is an existing file, but no -f given + if (exists && stats.isFile() && !options.force) + error('dest file already exists: ' + dest); + + sources.forEach(function(src) { + if (!fs.existsSync(src)) { + error('no such file or directory: '+src, true); + return; // skip file + } + + // If here, src exists + + // When copying to '/path/dir': + // thisDest = '/path/dir/file1' + var thisDest = dest; + if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) + thisDest = path.normalize(dest + '/' + path.basename(src)); + + if (fs.existsSync(thisDest) && !options.force) { + error('dest file already exists: ' + thisDest, true); + return; // skip file + } + + if (path.resolve(src) === path.dirname(path.resolve(thisDest))) { + error('cannot move to self: '+src, true); + return; // skip file + } + + fs.renameSync(src, thisDest); + }); // forEach(src) +} // mv +exports.mv = wrap('mv', _mv); + +//@ +//@ ### mkdir([options ,] dir [, dir ...]) +//@ ### mkdir([options ,] dir_array) +//@ Available options: +//@ +//@ + `p`: full path (will create intermediate dirs if necessary) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g'); +//@ mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above +//@ ``` +//@ +//@ Creates directories. +function _mkdir(options, dirs) { + options = parseOptions(options, { + 'p': 'fullpath' + }); + if (!dirs) + error('no paths given'); + + if (typeof dirs === 'string') + dirs = [].slice.call(arguments, 1); + // if it's array leave it as it is + + dirs.forEach(function(dir) { + if (fs.existsSync(dir)) { + if (!options.fullpath) + error('path already exists: ' + dir, true); + return; // skip dir + } + + // Base dir does not exist, and no -p option given + var baseDir = path.dirname(dir); + if (!fs.existsSync(baseDir) && !options.fullpath) { + error('no such file or directory: ' + baseDir, true); + return; // skip dir + } + + if (options.fullpath) + mkdirSyncRecursive(dir); + else + fs.mkdirSync(dir, parseInt('0777', 8)); + }); +} // mkdir +exports.mkdir = wrap('mkdir', _mkdir); + +//@ +//@ ### test(expression) +//@ Available expression primaries: +//@ +//@ + `'-b', 'path'`: true if path is a block device +//@ + `'-c', 'path'`: true if path is a character device +//@ + `'-d', 'path'`: true if path is a directory +//@ + `'-e', 'path'`: true if path exists +//@ + `'-f', 'path'`: true if path is a regular file +//@ + `'-L', 'path'`: true if path is a symboilc link +//@ + `'-p', 'path'`: true if path is a pipe (FIFO) +//@ + `'-S', 'path'`: true if path is a socket +//@ +//@ Examples: +//@ +//@ ```javascript +//@ if (test('-d', path)) { /* do something with dir */ }; +//@ if (!test('-f', path)) continue; // skip if it's a regular file +//@ ``` +//@ +//@ Evaluates expression using the available primaries and returns corresponding value. +function _test(options, path) { + if (!path) + error('no path given'); + + // hack - only works with unary primaries + options = parseOptions(options, { + 'b': 'block', + 'c': 'character', + 'd': 'directory', + 'e': 'exists', + 'f': 'file', + 'L': 'link', + 'p': 'pipe', + 'S': 'socket' + }); + + var canInterpret = false; + for (var key in options) + if (options[key] === true) { + canInterpret = true; + break; + } + + if (!canInterpret) + error('could not interpret expression'); + + if (options.link) { + try { + return fs.lstatSync(path).isSymbolicLink(); + } catch(e) { + return false; + } + } + + if (!fs.existsSync(path)) + return false; + + if (options.exists) + return true; + + var stats = fs.statSync(path); + + if (options.block) + return stats.isBlockDevice(); + + if (options.character) + return stats.isCharacterDevice(); + + if (options.directory) + return stats.isDirectory(); + + if (options.file) + return stats.isFile(); + + if (options.pipe) + return stats.isFIFO(); + + if (options.socket) + return stats.isSocket(); +} // test +exports.test = wrap('test', _test); + + +//@ +//@ ### cat(file [, file ...]) +//@ ### cat(file_array) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ var str = cat('file*.txt'); +//@ var str = cat('file1', 'file2'); +//@ var str = cat(['file1', 'file2']); // same as above +//@ ``` +//@ +//@ Returns a string containing the given file, or a concatenated string +//@ containing the files if more than one file is given (a new line character is +//@ introduced between each file). Wildcard `*` accepted. +function _cat(options, files) { + var cat = ''; + + if (!files) + error('no paths given'); + + if (typeof files === 'string') + files = [].slice.call(arguments, 1); + // if it's array leave it as it is + + files = expand(files); + + files.forEach(function(file) { + if (!fs.existsSync(file)) + error('no such file or directory: ' + file); + + cat += fs.readFileSync(file, 'utf8') + '\n'; + }); + + if (cat[cat.length-1] === '\n') + cat = cat.substring(0, cat.length-1); + + return ShellString(cat); +} +exports.cat = wrap('cat', _cat); + +//@ +//@ ### 'string'.to(file) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ cat('input.txt').to('output.txt'); +//@ ``` +//@ +//@ Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as +//@ those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_ +function _to(options, file) { + if (!file) + error('wrong arguments'); + + if (!fs.existsSync( path.dirname(file) )) + error('no such file or directory: ' + path.dirname(file)); + + try { + fs.writeFileSync(file, this.toString(), 'utf8'); + } catch(e) { + error('could not write to file (code '+e.code+'): '+file, true); + } +} +// In the future, when Proxies are default, we can add methods like `.to()` to primitive strings. +// For now, this is a dummy function to bookmark places we need such strings +function ShellString(str) { + return str; +} +String.prototype.to = wrap('to', _to); + +//@ +//@ ### sed([options ,] search_regex, replace_str, file) +//@ Available options: +//@ +//@ + `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_ +//@ +//@ Examples: +//@ +//@ ```javascript +//@ sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js'); +//@ sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js'); +//@ ``` +//@ +//@ Reads an input string from `file` and performs a JavaScript `replace()` on the input +//@ using the given search regex and replacement string. Returns the new string after replacement. +function _sed(options, regex, replacement, file) { + options = parseOptions(options, { + 'i': 'inplace' + }); + + if (typeof replacement === 'string') + replacement = replacement; // no-op + else if (typeof replacement === 'number') + replacement = replacement.toString(); // fallback + else + error('invalid replacement string'); + + if (!file) + error('no file given'); + + if (!fs.existsSync(file)) + error('no such file or directory: ' + file); + + var result = fs.readFileSync(file, 'utf8').replace(regex, replacement); + if (options.inplace) + fs.writeFileSync(file, result, 'utf8'); + + return ShellString(result); +} +exports.sed = wrap('sed', _sed); + +//@ +//@ ### grep([options ,] regex_filter, file [, file ...]) +//@ ### grep([options ,] regex_filter, file_array) +//@ Available options: +//@ +//@ + `-v`: Inverse the sense of the regex and print the lines not matching the criteria. +//@ +//@ Examples: +//@ +//@ ```javascript +//@ grep('-v', 'GLOBAL_VARIABLE', '*.js'); +//@ grep('GLOBAL_VARIABLE', '*.js'); +//@ ``` +//@ +//@ Reads input string from given files and returns a string containing all lines of the +//@ file that match the given `regex_filter`. Wildcard `*` accepted. +function _grep(options, regex, files) { + options = parseOptions(options, { + 'v': 'inverse' + }); + + if (!files) + error('no paths given'); + + if (typeof files === 'string') + files = [].slice.call(arguments, 2); + // if it's array leave it as it is + + files = expand(files); + + var grep = ''; + files.forEach(function(file) { + if (!fs.existsSync(file)) { + error('no such file or directory: ' + file, true); + return; + } + + var contents = fs.readFileSync(file, 'utf8'), + lines = contents.split(/\r*\n/); + lines.forEach(function(line) { + var matched = line.match(regex); + if ((options.inverse && !matched) || (!options.inverse && matched)) + grep += line + '\n'; + }); + }); + + return ShellString(grep); +} +exports.grep = wrap('grep', _grep); + + +//@ +//@ ### which(command) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ var nodeExec = which('node'); +//@ ``` +//@ +//@ Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions. +//@ Returns string containing the absolute path to the command. +function _which(options, cmd) { + if (!cmd) + error('must specify command'); + + var pathEnv = process.env.path || process.env.Path || process.env.PATH, + pathArray = splitPath(pathEnv), + where = null; + + // No relative/absolute paths provided? + if (cmd.search(/\//) === -1) { + // Search for command in PATH + pathArray.forEach(function(dir) { + if (where) + return; // already found it + + var attempt = path.resolve(dir + '/' + cmd); + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + + if (platform === 'win') { + var baseAttempt = attempt; + attempt = baseAttempt + '.exe'; + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + attempt = baseAttempt + '.cmd'; + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + attempt = baseAttempt + '.bat'; + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + } // if 'win' + }); + } + + // Command not found anywhere? + if (!fs.existsSync(cmd) && !where) + return null; + + where = where || path.resolve(cmd); + + return ShellString(where); +} +exports.which = wrap('which', _which); + +//@ +//@ ### echo(string [,string ...]) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ echo('hello world'); +//@ var str = echo('hello world'); +//@ ``` +//@ +//@ Prints string to stdout, and returns string with additional utility methods +//@ like `.to()`. +function _echo() { + var messages = [].slice.call(arguments, 0); + console.log.apply(this, messages); + return ShellString(messages.join(' ')); +} +exports.echo = _echo; // don't wrap() as it could parse '-options' + +// Pushd/popd/dirs internals +var _dirStack = []; + +function _isStackIndex(index) { + return (/^[\-+]\d+$/).test(index); +} + +function _parseStackIndex(index) { + if (_isStackIndex(index)) { + if (Math.abs(index) < _dirStack.length + 1) { // +1 for pwd + return (/^-/).test(index) ? Number(index) - 1 : Number(index); + } else { + error(index + ': directory stack index out of range'); + } + } else { + error(index + ': invalid number'); + } +} + +function _actualDirStack() { + return [process.cwd()].concat(_dirStack); +} + +//@ +//@ ### dirs([options | '+N' | '-N']) +//@ +//@ Available options: +//@ +//@ + `-c`: Clears the directory stack by deleting all of the elements. +//@ +//@ Arguments: +//@ +//@ + `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero. +//@ + `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero. +//@ +//@ Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified. +//@ +//@ See also: pushd, popd +function _dirs(options, index) { + if (_isStackIndex(options)) { + index = options; + options = ''; + } + + options = parseOptions(options, { + 'c' : 'clear' + }); + + if (options['clear']) { + _dirStack = []; + return _dirStack; + } + + var stack = _actualDirStack(); + + if (index) { + index = _parseStackIndex(index); + + if (index < 0) { + index = stack.length + index; + } + + log(stack[index]); + return stack[index]; + } + + log(stack.join(' ')); + + return stack; +} +exports.dirs = wrap("dirs", _dirs); + +//@ +//@ ### pushd([options,] [dir | '-N' | '+N']) +//@ +//@ Available options: +//@ +//@ + `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated. +//@ +//@ Arguments: +//@ +//@ + `dir`: Makes the current working directory be the top of the stack, and then executes the equivalent of `cd dir`. +//@ + `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. +//@ + `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. +//@ +//@ Examples: +//@ +//@ ```javascript +//@ // process.cwd() === '/usr' +//@ pushd('/etc'); // Returns /etc /usr +//@ pushd('+1'); // Returns /usr /etc +//@ ``` +//@ +//@ Save the current directory on the top of the directory stack and then cd to `dir`. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. +function _pushd(options, dir) { + if (_isStackIndex(options)) { + dir = options; + options = ''; + } + + options = parseOptions(options, { + 'n' : 'no-cd' + }); + + var dirs = _actualDirStack(); + + if (dir === '+0') { + return dirs; // +0 is a noop + } else if (!dir) { + if (dirs.length > 1) { + dirs = dirs.splice(1, 1).concat(dirs); + } else { + return error('no other directory'); + } + } else if (_isStackIndex(dir)) { + var n = _parseStackIndex(dir); + dirs = dirs.slice(n).concat(dirs.slice(0, n)); + } else { + if (options['no-cd']) { + dirs.splice(1, 0, dir); + } else { + dirs.unshift(dir); + } + } + + if (options['no-cd']) { + dirs = dirs.slice(1); + } else { + dir = path.resolve(dirs.shift()); + _cd('', dir); + } + + _dirStack = dirs; + return _dirs(''); +} +exports.pushd = wrap('pushd', _pushd); + +//@ +//@ ### popd([options,] ['-N' | '+N']) +//@ +//@ Available options: +//@ +//@ + `-n`: Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated. +//@ +//@ Arguments: +//@ +//@ + `+N`: Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero. +//@ + `-N`: Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero. +//@ +//@ Examples: +//@ +//@ ```javascript +//@ echo(process.cwd()); // '/usr' +//@ pushd('/etc'); // '/etc /usr' +//@ echo(process.cwd()); // '/etc' +//@ popd(); // '/usr' +//@ echo(process.cwd()); // '/usr' +//@ ``` +//@ +//@ When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. +function _popd(options, index) { + if (_isStackIndex(options)) { + index = options; + options = ''; + } + + options = parseOptions(options, { + 'n' : 'no-cd' + }); + + if (!_dirStack.length) { + return error('directory stack empty'); + } + + index = _parseStackIndex(index || '+0'); + + if (options['no-cd'] || index > 0 || _dirStack.length + index === 0) { + index = index > 0 ? index - 1 : index; + _dirStack.splice(index, 1); + } else { + var dir = path.resolve(_dirStack.shift()); + _cd('', dir); + } + + return _dirs(''); +} +exports.popd = wrap("popd", _popd); + +//@ +//@ ### exit(code) +//@ Exits the current process with the given exit code. +exports.exit = process.exit; + +//@ +//@ ### env['VAR_NAME'] +//@ Object containing environment variables (both getter and setter). Shortcut to process.env. +exports.env = process.env; + +//@ +//@ ### exec(command [, options] [, callback]) +//@ Available options (all `false` by default): +//@ +//@ + `async`: Asynchronous execution. Defaults to true if a callback is provided. +//@ + `silent`: Do not echo program output to console. +//@ +//@ Examples: +//@ +//@ ```javascript +//@ var version = exec('node --version', {silent:true}).output; +//@ +//@ var child = exec('some_long_running_process', {async:true}); +//@ child.stdout.on('data', function(data) { +//@ /* ... do something with data ... */ +//@ }); +//@ +//@ exec('some_long_running_process', function(code, output) { +//@ console.log('Exit code:', code); +//@ console.log('Program output:', output); +//@ }); +//@ ``` +//@ +//@ Executes the given `command` _synchronously_, unless otherwise specified. +//@ When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's +//@ `output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and +//@ the `callback` gets the arguments `(code, output)`. +//@ +//@ **Note:** For long-lived processes, it's best to run `exec()` asynchronously as +//@ the current synchronous implementation uses a lot of CPU. This should be getting +//@ fixed soon. +function _exec(command, options, callback) { + if (!command) + error('must specify command'); + + // Callback is defined instead of options. + if (typeof options === 'function') { + callback = options; + options = { async: true }; + } + + // Callback is defined with options. + if (typeof options === 'object' && typeof callback === 'function') { + options.async = true; + } + + options = extend({ + silent: config.silent, + async: false + }, options); + + if (options.async) + return execAsync(command, options, callback); + else + return execSync(command, options); +} +exports.exec = wrap('exec', _exec, {notUnix:true}); + +var PERMS = (function (base) { + return { + OTHER_EXEC : base.EXEC, + OTHER_WRITE : base.WRITE, + OTHER_READ : base.READ, + + GROUP_EXEC : base.EXEC << 3, + GROUP_WRITE : base.WRITE << 3, + GROUP_READ : base.READ << 3, + + OWNER_EXEC : base.EXEC << 6, + OWNER_WRITE : base.WRITE << 6, + OWNER_READ : base.READ << 6, + + // Literal octal numbers are apparently not allowed in "strict" javascript. Using parseInt is + // the preferred way, else a jshint warning is thrown. + STICKY : parseInt('01000', 8), + SETGID : parseInt('02000', 8), + SETUID : parseInt('04000', 8), + + TYPE_MASK : parseInt('0770000', 8) + }; +})({ + EXEC : 1, + WRITE : 2, + READ : 4 +}); + + +//@ +//@ ### chmod(octal_mode || octal_string, file) +//@ ### chmod(symbolic_mode, file) +//@ +//@ Available options: +//@ +//@ + `-v`: output a diagnostic for every file processed//@ +//@ + `-c`: like verbose but report only when a change is made//@ +//@ + `-R`: change files and directories recursively//@ +//@ +//@ Examples: +//@ +//@ ```javascript +//@ chmod(755, '/Users/brandon'); +//@ chmod('755', '/Users/brandon'); // same as above +//@ chmod('u+x', '/Users/brandon'); +//@ ``` +//@ +//@ Alters the permissions of a file or directory by either specifying the +//@ absolute permissions in octal form or expressing the changes in symbols. +//@ This command tries to mimic the POSIX behavior as much as possible. +//@ Notable exceptions: +//@ +//@ + In symbolic modes, 'a-r' and '-r' are identical. No consideration is +//@ given to the umask. +//@ + There is no "quiet" option since default behavior is to run silent. +function _chmod(options, mode, filePattern) { + if (!filePattern) { + if (options.length > 0 && options.charAt(0) === '-') { + // Special case where the specified file permissions started with - to subtract perms, which + // get picked up by the option parser as command flags. + // If we are down by one argument and options starts with -, shift everything over. + filePattern = mode; + mode = options; + options = ''; + } + else { + error('You must specify a file.'); + } + } + + options = parseOptions(options, { + 'R': 'recursive', + 'c': 'changes', + 'v': 'verbose' + }); + + if (typeof filePattern === 'string') { + filePattern = [ filePattern ]; + } + + var files; + + if (options.recursive) { + files = []; + expand(filePattern).forEach(function addFile(expandedFile) { + var stat = fs.lstatSync(expandedFile); + + if (!stat.isSymbolicLink()) { + files.push(expandedFile); + + if (stat.isDirectory()) { // intentionally does not follow symlinks. + fs.readdirSync(expandedFile).forEach(function (child) { + addFile(expandedFile + '/' + child); + }); + } + } + }); + } + else { + files = expand(filePattern); + } + + files.forEach(function innerChmod(file) { + file = path.resolve(file); + if (!fs.existsSync(file)) { + error('File not found: ' + file); + } + + // When recursing, don't follow symlinks. + if (options.recursive && fs.lstatSync(file).isSymbolicLink()) { + return; + } + + var perms = fs.statSync(file).mode; + var type = perms & PERMS.TYPE_MASK; + + var newPerms = perms; + + if (isNaN(parseInt(mode, 8))) { + // parse options + mode.split(',').forEach(function (symbolicMode) { + /*jshint regexdash:true */ + var pattern = /([ugoa]*)([=\+-])([rwxXst]*)/i; + var matches = pattern.exec(symbolicMode); + + if (matches) { + var applyTo = matches[1]; + var operator = matches[2]; + var change = matches[3]; + + var changeOwner = applyTo.indexOf('u') != -1 || applyTo === 'a' || applyTo === ''; + var changeGroup = applyTo.indexOf('g') != -1 || applyTo === 'a' || applyTo === ''; + var changeOther = applyTo.indexOf('o') != -1 || applyTo === 'a' || applyTo === ''; + + var changeRead = change.indexOf('r') != -1; + var changeWrite = change.indexOf('w') != -1; + var changeExec = change.indexOf('x') != -1; + var changeSticky = change.indexOf('t') != -1; + var changeSetuid = change.indexOf('s') != -1; + + var mask = 0; + if (changeOwner) { + mask |= (changeRead ? PERMS.OWNER_READ : 0) + (changeWrite ? PERMS.OWNER_WRITE : 0) + (changeExec ? PERMS.OWNER_EXEC : 0) + (changeSetuid ? PERMS.SETUID : 0); + } + if (changeGroup) { + mask |= (changeRead ? PERMS.GROUP_READ : 0) + (changeWrite ? PERMS.GROUP_WRITE : 0) + (changeExec ? PERMS.GROUP_EXEC : 0) + (changeSetuid ? PERMS.SETGID : 0); + } + if (changeOther) { + mask |= (changeRead ? PERMS.OTHER_READ : 0) + (changeWrite ? PERMS.OTHER_WRITE : 0) + (changeExec ? PERMS.OTHER_EXEC : 0); + } + + // Sticky bit is special - it's not tied to user, group or other. + if (changeSticky) { + mask |= PERMS.STICKY; + } + + switch (operator) { + case '+': + newPerms |= mask; + break; + + case '-': + newPerms &= ~mask; + break; + + case '=': + newPerms = type + mask; + + // According to POSIX, when using = to explicitly set the permissions, setuid and setgid can never be cleared. + if (fs.statSync(file).isDirectory()) { + newPerms |= (PERMS.SETUID + PERMS.SETGID) & perms; + } + break; + } + + if (options.verbose) { + log(file + ' -> ' + newPerms.toString(8)); + } + + if (perms != newPerms) { + if (!options.verbose && options.changes) { + log(file + ' -> ' + newPerms.toString(8)); + } + fs.chmodSync(file, newPerms); + } + } + else { + error('Invalid symbolic mode change: ' + symbolicMode); + } + }); + } + else { + // they gave us a full number + newPerms = type + parseInt(mode, 8); + + // POSIX rules are that setuid and setgid can only be added using numeric form, but not cleared. + if (fs.statSync(file).isDirectory()) { + newPerms |= (PERMS.SETUID + PERMS.SETGID) & perms; + } + + fs.chmodSync(file, newPerms); + } + }); +} +exports.chmod = wrap('chmod', _chmod); + + +//@ +//@ ## Configuration +//@ + + + +exports.config = config; + +//@ +//@ ### config.silent +//@ Example: +//@ +//@ ```javascript +//@ var silentState = config.silent; // save old silent state +//@ config.silent = true; +//@ /* ... */ +//@ config.silent = silentState; // restore old silent state +//@ ``` +//@ +//@ Suppresses all command output if `true`, except for `echo()` calls. +//@ Default is `false`. + +//@ +//@ ### config.fatal +//@ Example: +//@ +//@ ```javascript +//@ config.fatal = true; +//@ cp('this_file_does_not_exist', '/dev/null'); // dies here +//@ /* more commands... */ +//@ ``` +//@ +//@ If `true` the script will die on errors. Default is `false`. + + + + +//@ +//@ ## Non-Unix commands +//@ + + + + + + +//@ +//@ ### tempdir() +//@ Searches and returns string containing a writeable, platform-dependent temporary directory. +//@ Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). +exports.tempdir = wrap('tempdir', tempDir); + + +//@ +//@ ### error() +//@ Tests if error occurred in the last command. Returns `null` if no error occurred, +//@ otherwise returns string explaining the error +exports.error = function() { + return state.error; +}; + + + + + +//////////////////////////////////////////////////////////////////////////////////////////////// +// +// Auxiliary functions (internal use only) +// + +function log() { + if (!config.silent) + console.log.apply(this, arguments); +} + +function deprecate(what, msg) { + console.log('*** ShellJS.'+what+': This function is deprecated.', msg); +} + +function write(msg) { + if (!config.silent) + process.stdout.write(msg); +} + +// Shows error message. Throws unless _continue or config.fatal are true +function error(msg, _continue) { + if (state.error === null) + state.error = ''; + state.error += state.currentCmd + ': ' + msg + '\n'; + + log(state.error); + + if (config.fatal) + process.exit(1); + + if (!_continue) + throw ''; +} + +// Returns {'alice': true, 'bob': false} when passed: +// parseOptions('-a', {'a':'alice', 'b':'bob'}); +function parseOptions(str, map) { + if (!map) + error('parseOptions() internal error: no map given'); + + // All options are false by default + var options = {}; + for (var letter in map) + options[map[letter]] = false; + + if (!str) + return options; // defaults + + if (typeof str !== 'string') + error('parseOptions() internal error: wrong str'); + + // e.g. match[1] = 'Rf' for str = '-Rf' + var match = str.match(/^\-(.+)/); + if (!match) + return options; + + // e.g. chars = ['R', 'f'] + var chars = match[1].split(''); + + chars.forEach(function(c) { + if (c in map) + options[map[c]] = true; + else + error('option not recognized: '+c); + }); + + return options; +} + +// Common wrapper for all Unix-like commands +function wrap(cmd, fn, options) { + return function() { + var retValue = null; + + state.currentCmd = cmd; + state.error = null; + + try { + var args = [].slice.call(arguments, 0); + + if (options && options.notUnix) { + retValue = fn.apply(this, args); + } else { + if (args.length === 0 || typeof args[0] !== 'string' || args[0][0] !== '-') + args.unshift(''); // only add dummy option if '-option' not already present + retValue = fn.apply(this, args); + } + } catch (e) { + if (!state.error) { + // If state.error hasn't been set it's an error thrown by Node, not us - probably a bug... + console.log('shell.js: internal error'); + console.log(e.stack || e); + process.exit(1); + } + if (config.fatal) + throw e; + } + + state.currentCmd = 'shell.js'; + return retValue; + }; +} // wrap + +// Buffered file copy, synchronous +// (Using readFileSync() + writeFileSync() could easily cause a memory overflow +// with large files) +function copyFileSync(srcFile, destFile) { + if (!fs.existsSync(srcFile)) + error('copyFileSync: no such file or directory: ' + srcFile); + + var BUF_LENGTH = 64*1024, + buf = new Buffer(BUF_LENGTH), + bytesRead = BUF_LENGTH, + pos = 0, + fdr = null, + fdw = null; + + try { + fdr = fs.openSync(srcFile, 'r'); + } catch(e) { + error('copyFileSync: could not read src file ('+srcFile+')'); + } + + try { + fdw = fs.openSync(destFile, 'w'); + } catch(e) { + error('copyFileSync: could not write to dest file (code='+e.code+'):'+destFile); + } + + while (bytesRead === BUF_LENGTH) { + bytesRead = fs.readSync(fdr, buf, 0, BUF_LENGTH, pos); + fs.writeSync(fdw, buf, 0, bytesRead); + pos += bytesRead; + } + + fs.closeSync(fdr); + fs.closeSync(fdw); +} + +// Recursively copies 'sourceDir' into 'destDir' +// Adapted from https://github.com/ryanmcgrath/wrench-js +// +// Copyright (c) 2010 Ryan McGrath +// Copyright (c) 2012 Artur Adib +// +// Licensed under the MIT License +// http://www.opensource.org/licenses/mit-license.php +function cpdirSyncRecursive(sourceDir, destDir, opts) { + if (!opts) opts = {}; + + /* 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(destDir, checkDir.mode); + } catch (e) { + //if the directory already exists, that's okay + if (e.code !== 'EEXIST') throw e; + } + + var files = fs.readdirSync(sourceDir); + + for(var i = 0; i < files.length; i++) { + var currFile = fs.lstatSync(sourceDir + "/" + files[i]); + + if (currFile.isDirectory()) { + /* recursion this thing right on back. */ + cpdirSyncRecursive(sourceDir + "/" + files[i], destDir + "/" + files[i], opts); + } else if (currFile.isSymbolicLink()) { + var symlinkFull = fs.readlinkSync(sourceDir + "/" + files[i]); + fs.symlinkSync(symlinkFull, destDir + "/" + files[i]); + } else { + /* At this point, we've hit a file actually worth copying... so copy it on over. */ + if (fs.existsSync(destDir + "/" + files[i]) && !opts.force) { + log('skipping existing file: ' + files[i]); + } else { + copyFileSync(sourceDir + "/" + files[i], destDir + "/" + files[i]); + } + } + + } // for files +} // cpdirSyncRecursive + +// Recursively removes 'dir' +// Adapted from https://github.com/ryanmcgrath/wrench-js +// +// Copyright (c) 2010 Ryan McGrath +// Copyright (c) 2012 Artur Adib +// +// Licensed under the MIT License +// http://www.opensource.org/licenses/mit-license.php +function rmdirSyncRecursive(dir, force) { + var files; + + files = fs.readdirSync(dir); + + // Loop through and delete everything in the sub-tree after checking it + for(var i = 0; i < files.length; i++) { + var file = dir + "/" + files[i], + currFile = fs.lstatSync(file); + + if(currFile.isDirectory()) { // Recursive function back to the beginning + rmdirSyncRecursive(file, force); + } + + else if(currFile.isSymbolicLink()) { // Unlink symlinks + if (force || isWriteable(file)) { + try { + _unlinkSync(file); + } catch (e) { + error('could not remove file (code '+e.code+'): ' + file, true); + } + } + } + + else // Assume it's a file - perhaps a try/catch belongs here? + if (force || isWriteable(file)) { + try { + _unlinkSync(file); + } catch (e) { + error('could not remove file (code '+e.code+'): ' + file, true); + } + } + } + + // Now that we know everything in the sub-tree has been deleted, we can delete the main directory. + // Huzzah for the shopkeep. + + var result; + try { + result = fs.rmdirSync(dir); + } catch(e) { + error('could not remove directory (code '+e.code+'): ' + dir, true); + } + + return result; +} // rmdirSyncRecursive + +// Recursively creates 'dir' +function mkdirSyncRecursive(dir) { + var baseDir = path.dirname(dir); + + // Base dir exists, no recursion necessary + if (fs.existsSync(baseDir)) { + fs.mkdirSync(dir, parseInt('0777', 8)); + return; + } + + // Base dir does not exist, go recursive + mkdirSyncRecursive(baseDir); + + // Base dir created, can create dir + fs.mkdirSync(dir, parseInt('0777', 8)); +} + +// e.g. 'shelljs_a5f185d0443ca...' +function randomFileName() { + function randomHash(count) { + if (count === 1) + return parseInt(16*Math.random(), 10).toString(16); + else { + var hash = ''; + for (var i=0; i<count; i++) + hash += randomHash(1); + return hash; + } + } + + return 'shelljs_'+randomHash(20); +} + +// Returns false if 'dir' is not a writeable directory, 'dir' otherwise +function writeableDir(dir) { + if (!dir || !fs.existsSync(dir)) + return false; + + if (!fs.statSync(dir).isDirectory()) + return false; + + var testFile = dir+'/'+randomFileName(); + try { + fs.writeFileSync(testFile, ' '); + _unlinkSync(testFile); + return dir; + } catch (e) { + return false; + } +} + +// Cross-platform method for getting an available temporary directory. +// Follows the algorithm of Python's tempfile.tempdir +// http://docs.python.org/library/tempfile.html#tempfile.tempdir +function tempDir() { + if (state.tempDir) + return state.tempDir; // from cache + + state.tempDir = writeableDir(process.env['TMPDIR']) || + writeableDir(process.env['TEMP']) || + writeableDir(process.env['TMP']) || + writeableDir(process.env['Wimp$ScrapDir']) || // RiscOS + writeableDir('C:\\TEMP') || // Windows + writeableDir('C:\\TMP') || // Windows + writeableDir('\\TEMP') || // Windows + writeableDir('\\TMP') || // Windows + writeableDir('/tmp') || + writeableDir('/var/tmp') || + writeableDir('/usr/tmp') || + writeableDir('.'); // last resort + + return state.tempDir; +} + +// Wrapper around exec() to enable echoing output to console in real time +function execAsync(cmd, opts, callback) { + var output = ''; + + var options = extend({ + silent: config.silent + }, opts); + + var c = child.exec(cmd, {env: process.env, maxBuffer: 20*1024*1024}, function(err) { + if (callback) + callback(err ? err.code : 0, output); + }); + + c.stdout.on('data', function(data) { + output += data; + if (!options.silent) + process.stdout.write(data); + }); + + c.stderr.on('data', function(data) { + output += data; + if (!options.silent) + process.stdout.write(data); + }); + + return c; +} + +// Hack to run child_process.exec() synchronously (sync avoids callback hell) +// Uses a custom wait loop that checks for a flag file, created when the child process is done. +// (Can't do a wait loop that checks for internal Node variables/messages as +// Node is single-threaded; callbacks and other internal state changes are done in the +// event loop). +function execSync(cmd, opts) { + var stdoutFile = path.resolve(tempDir()+'/'+randomFileName()), + codeFile = path.resolve(tempDir()+'/'+randomFileName()), + scriptFile = path.resolve(tempDir()+'/'+randomFileName()), + sleepFile = path.resolve(tempDir()+'/'+randomFileName()); + + var options = extend({ + silent: config.silent + }, opts); + + var previousStdoutContent = ''; + // Echoes stdout changes from running process, if not silent + function updateStdout() { + if (options.silent || !fs.existsSync(stdoutFile)) + return; + + var stdoutContent = fs.readFileSync(stdoutFile, 'utf8'); + // No changes since last time? + if (stdoutContent.length <= previousStdoutContent.length) + return; + + process.stdout.write(stdoutContent.substr(previousStdoutContent.length)); + previousStdoutContent = stdoutContent; + } + + function escape(str) { + return (str+'').replace(/([\\"'])/g, "\\$1").replace(/\0/g, "\\0"); + } + + cmd += ' > '+stdoutFile+' 2>&1'; // works on both win/unix + + var script = + "var child = require('child_process')," + + " fs = require('fs');" + + "child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {" + + " fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');" + + "});"; + + if (fs.existsSync(scriptFile)) _unlinkSync(scriptFile); + if (fs.existsSync(stdoutFile)) _unlinkSync(stdoutFile); + if (fs.existsSync(codeFile)) _unlinkSync(codeFile); + + fs.writeFileSync(scriptFile, script); + child.exec('"'+process.execPath+'" '+scriptFile, { + env: process.env, + cwd: exports.pwd(), + maxBuffer: 20*1024*1024 + }); + + // The wait loop + // sleepFile is used as a dummy I/O op to mitigate unnecessary CPU usage + // (tried many I/O sync ops, writeFileSync() seems to be only one that is effective in reducing + // CPU usage, though apparently not so much on Windows) + while (!fs.existsSync(codeFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); } + while (!fs.existsSync(stdoutFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); } + + // At this point codeFile exists, but it's not necessarily flushed yet. + // Keep reading it until it is. + var code = parseInt('', 10); + while (isNaN(code)) { + code = parseInt(fs.readFileSync(codeFile, 'utf8'), 10); + } + + var stdout = fs.readFileSync(stdoutFile, 'utf8'); + + // No biggie if we can't erase the files now -- they're in a temp dir anyway + try { _unlinkSync(scriptFile); } catch(e) {} + try { _unlinkSync(stdoutFile); } catch(e) {} + try { _unlinkSync(codeFile); } catch(e) {} + try { _unlinkSync(sleepFile); } catch(e) {} + + // True if successful, false if not + var obj = { + code: code, + output: stdout + }; + return obj; +} // execSync() + +// Expands wildcards with matching file names. For a given array of file names 'list', returns +// another array containing all file names as per ls(list[i]). +// For example: +// expand(['file*.js']) = ['file1.js', 'file2.js', ...] +// (if the files 'file1.js', 'file2.js', etc, exist in the current dir) +function expand(list) { + var expanded = []; + list.forEach(function(listEl) { + // Wildcard present? + if (listEl.search(/\*/) > -1) { + _ls('', listEl).forEach(function(file) { + expanded.push(file); + }); + } else { + expanded.push(listEl); + } + }); + return expanded; +} + +// Cross-platform method for splitting environment PATH variables +function splitPath(p) { + if (!p) + return []; + + if (platform === 'win') + return p.split(';'); + else + return p.split(':'); +} + +// extend(target_obj, source_obj1 [, source_obj2 ...]) +// Shallow extend, e.g.: +// extend({A:1}, {b:2}, {c:3}) returns {A:1, b:2, c:3} +function extend(target) { + var sources = [].slice.call(arguments, 1); + sources.forEach(function(source) { + for (var key in source) + target[key] = source[key]; + }); + + return target; +} + +// Normalizes _unlinkSync() across platforms to match Unix behavior, i.e. +// file can be unlinked even if it's read-only, see joyent/node#3006 +function _unlinkSync(file) { + try { + fs.unlinkSync(file); + } catch(e) { + // Try to override file permission + if (e.code === 'EPERM') { + fs.chmodSync(file, '0666'); + fs.unlinkSync(file); + } else { + throw e; + } + } +} + +// Hack to determine if file has write permissions for current user +// Avoids having to check user, group, etc, but it's probably slow +function isWriteable(file) { + var writePermission = true; + try { + var __fd = fs.openSync(file, 'a'); + fs.closeSync(__fd); + } catch(e) { + writePermission = false; + } + + return writePermission; +}
http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/.npmignore ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/.npmignore b/node_modules/shelljs/test/.npmignore new file mode 100644 index 0000000..a1632ab --- /dev/null +++ b/node_modules/shelljs/test/.npmignore @@ -0,0 +1,2 @@ +tmp/ + http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/cat.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/cat.js b/node_modules/shelljs/test/cat.js new file mode 100644 index 0000000..d0d9ddb --- /dev/null +++ b/node_modules/shelljs/test/cat.js @@ -0,0 +1,57 @@ +var shell = require('..'); + +var assert = require('assert'), + path = require('path'), + fs = require('fs'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +shell.config.silent = true; + +function numLines(str) { + return typeof str === 'string' ? str.match(/\n/g).length : 0; +} + +// save current dir +var cur = shell.pwd(); + +shell.rm('-rf', 'tmp'); +shell.mkdir('tmp'); + +// +// Invalids +// + +shell.cat(); +assert.ok(shell.error()); + +assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check +shell.cat('/adsfasdf'); // file does not exist +assert.ok(shell.error()); + +// +// Valids +// + +// simple +var result = shell.cat('resources/file1'); +assert.equal(shell.error(), null); +assert.equal(result, 'test1'); + +// multiple files +var result = shell.cat('resources/file2', 'resources/file1'); +assert.equal(shell.error(), null); +assert.equal(result, 'test2\ntest1'); + +// multiple files, array syntax +var result = shell.cat(['resources/file2', 'resources/file1']); +assert.equal(shell.error(), null); +assert.equal(result, 'test2\ntest1'); + +var result = shell.cat('resources/file*.txt'); +assert.equal(shell.error(), null); +assert.ok(result.search('test1') > -1); // file order might be random +assert.ok(result.search('test2') > -1); + +shell.exit(123); http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/cd.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/cd.js b/node_modules/shelljs/test/cd.js new file mode 100644 index 0000000..e6f6c38 --- /dev/null +++ b/node_modules/shelljs/test/cd.js @@ -0,0 +1,64 @@ +var shell = require('..'); + +var assert = require('assert'), + path = require('path'), + fs = require('fs'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +shell.config.silent = true; + +function numLines(str) { + return typeof str === 'string' ? str.match(/\n/g).length : 0; +} + +// save current dir +var cur = shell.pwd(); + +shell.rm('-rf', 'tmp'); +shell.mkdir('tmp'); + +// +// Invalids +// + +shell.cd(); +assert.ok(shell.error()); + +assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check +shell.cd('/adsfasdf'); // dir does not exist +assert.ok(shell.error()); + +assert.equal(fs.existsSync('resources/file1'), true); // sanity check +shell.cd('resources/file1'); // file, not dir +assert.ok(shell.error()); + +// +// Valids +// + +shell.cd(cur); +shell.cd('tmp'); +assert.equal(shell.error(), null); +assert.equal(path.basename(process.cwd()), 'tmp'); + +shell.cd(cur); +shell.cd('/'); +assert.equal(shell.error(), null); +assert.equal(process.cwd(), path.resolve('/')); + +// cd + other commands + +shell.cd(cur); +shell.rm('-f', 'tmp/*'); +assert.equal(fs.existsSync('tmp/file1'), false); +shell.cd('resources'); +assert.equal(shell.error(), null); +shell.cp('file1', '../tmp'); +assert.equal(shell.error(), null); +shell.cd('../tmp'); +assert.equal(shell.error(), null); +assert.equal(fs.existsSync('file1'), true); + +shell.exit(123); http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/chmod.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/chmod.js b/node_modules/shelljs/test/chmod.js new file mode 100644 index 0000000..b31e25e --- /dev/null +++ b/node_modules/shelljs/test/chmod.js @@ -0,0 +1,81 @@ +var shell = require('..'); + +var assert = require('assert'), + path = require('path'), + fs = require('fs'); + +shell.config.silent = true; + +// +// Invalids +// + +shell.chmod('blah'); // missing args +assert.ok(shell.error()); +shell.chmod('893', 'resources/chmod'); // invalid permissions - mode must be in octal +assert.ok(shell.error()); + +// +// Valids +// + +// Test files - the bitmasking is to ignore the upper bits. +shell.chmod('755', 'resources/chmod/file1'); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('755', 8)); +shell.chmod('644', 'resources/chmod/file1'); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('644', 8)); + +shell.chmod('o+x', 'resources/chmod/file1'); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('007', 8), parseInt('005', 8)); +shell.chmod('644', 'resources/chmod/file1'); + +shell.chmod('+x', 'resources/chmod/file1'); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('755', 8)); +shell.chmod('644', 'resources/chmod/file1'); + +// Test setuid +shell.chmod('u+s', 'resources/chmod/file1'); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('4000', 8), parseInt('4000', 8)); +shell.chmod('u-s', 'resources/chmod/file1'); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('644', 8)); + +// according to POSIX standards at http://linux.die.net/man/1/chmod, +// setuid is never cleared from a directory unless explicitly asked for. +shell.chmod('u+s', 'resources/chmod/c'); +shell.chmod('755', 'resources/chmod/c'); +assert.equal(fs.statSync('resources/chmod/c').mode & parseInt('4000', 8), parseInt('4000', 8)); +shell.chmod('u-s', 'resources/chmod/c'); + +// Test setgid +shell.chmod('g+s', 'resources/chmod/file1'); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('2000', 8), parseInt('2000', 8)); +shell.chmod('g-s', 'resources/chmod/file1'); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('644', 8)); + +// Test sticky bit +shell.chmod('+t', 'resources/chmod/file1'); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('1000', 8), parseInt('1000', 8)); +shell.chmod('-t', 'resources/chmod/file1'); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('644', 8)); +assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('1000', 8), 0); + +// Test directories +shell.chmod('a-w', 'resources/chmod/b/a/b'); +assert.equal(fs.statSync('resources/chmod/b/a/b').mode & parseInt('777', 8), parseInt('555', 8)); +shell.chmod('755', 'resources/chmod/b/a/b'); + +// Test recursion +shell.chmod('-R', 'a+w', 'resources/chmod/b'); +assert.equal(fs.statSync('resources/chmod/b/a/b').mode & parseInt('777', 8), parseInt('777', 8)); +shell.chmod('-R', '755', 'resources/chmod/b'); +assert.equal(fs.statSync('resources/chmod/b/a/b').mode & parseInt('777', 8), parseInt('755', 8)); + +// Test symbolic links w/ recursion - WARNING: *nix only +fs.symlinkSync('resources/chmod/b/a', 'resources/chmod/a/b/c/link', 'dir'); +shell.chmod('-R', 'u-w', 'resources/chmod/a/b'); +assert.equal(fs.statSync('resources/chmod/a/b/c').mode & parseInt('700', 8), parseInt('500', 8)); +assert.equal(fs.statSync('resources/chmod/b/a').mode & parseInt('700', 8), parseInt('700', 8)); +shell.chmod('-R', 'u+w', 'resources/chmod/a/b'); +fs.unlinkSync('resources/chmod/a/b/c/link'); + +shell.exit(123); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/config.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/config.js b/node_modules/shelljs/test/config.js new file mode 100644 index 0000000..bd81ec0 --- /dev/null +++ b/node_modules/shelljs/test/config.js @@ -0,0 +1,50 @@ +var shell = require('..'); + +var assert = require('assert'), + child = require('child_process'); + +function numLines(str) { + return typeof str === 'string' ? str.match(/\n/g).length : 0; +} + +// +// config.silent +// + +assert.equal(shell.config.silent, false); // default + +shell.config.silent = true; +assert.equal(shell.config.silent, true); + +shell.config.silent = false; +assert.equal(shell.config.silent, false); + +// +// config.fatal +// + +assert.equal(shell.config.fatal, false); // default + +// +// config.fatal = false +// +shell.mkdir('-p', 'tmp'); +var file = 'tmp/tempscript'+Math.random()+'.js', + script = 'require(\'../../global.js\'); config.silent=true; config.fatal=false; cp("this_file_doesnt_exist", "."); echo("got here");'; +script.to(file); +child.exec('node '+file, function(err, stdout, stderr) { + assert.ok(stdout.match('got here')); + + // + // config.fatal = true + // + shell.mkdir('-p', 'tmp'); + var file = 'tmp/tempscript'+Math.random()+'.js', + script = 'require(\'../../global.js\'); config.silent=true; config.fatal=true; cp("this_file_doesnt_exist", "."); echo("got here");'; + script.to(file); + child.exec('node '+file, function(err, stdout, stderr) { + assert.ok(!stdout.match('got here')); + + shell.exit(123); + }); +}); http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/cp.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/cp.js b/node_modules/shelljs/test/cp.js new file mode 100644 index 0000000..39b3ba9 --- /dev/null +++ b/node_modules/shelljs/test/cp.js @@ -0,0 +1,143 @@ +var shell = require('..'); + +var assert = require('assert'), + path = require('path'), + fs = require('fs'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +shell.config.silent = true; + +function numLines(str) { + return typeof str === 'string' ? str.match(/\n/g).length : 0; +} + +shell.rm('-rf', 'tmp'); +shell.mkdir('tmp'); + +// +// Invalids +// + +shell.cp(); +assert.ok(shell.error()); + +shell.cp('file1'); +assert.ok(shell.error()); + +shell.cp('-f'); +assert.ok(shell.error()); + +shell.rm('-rf', 'tmp/*'); +shell.cp('-@', 'resources/file1', 'tmp/file1'); // option not supported, files OK +assert.ok(shell.error()); +assert.equal(fs.existsSync('tmp/file1'), false); + +shell.cp('-Z', 'asdfasdf', 'tmp/file2'); // option not supported, files NOT OK +assert.ok(shell.error()); +assert.equal(fs.existsSync('tmp/file2'), false); + +shell.cp('asdfasdf', 'tmp'); // source does not exist +assert.ok(shell.error()); +assert.equal(numLines(shell.error()), 1); +assert.equal(fs.existsSync('tmp/asdfasdf'), false); + +shell.cp('asdfasdf1', 'asdfasdf2', 'tmp'); // sources do not exist +assert.ok(shell.error()); +assert.equal(numLines(shell.error()), 2); +assert.equal(fs.existsSync('tmp/asdfasdf1'), false); +assert.equal(fs.existsSync('tmp/asdfasdf2'), false); + +shell.cp('asdfasdf1', 'asdfasdf2', 'resources/file1'); // too many sources (dest is file) +assert.ok(shell.error()); + +shell.cp('resources/file1', 'resources/file2'); // dest already exists +assert.ok(shell.error()); + +shell.cp('resources/file1', 'resources/file2', 'tmp/a_file'); // too many sources +assert.ok(shell.error()); +assert.equal(fs.existsSync('tmp/a_file'), false); + +// +// Valids +// + +// simple - to dir +shell.cp('resources/file1', 'tmp'); +assert.equal(shell.error(), null); +assert.equal(fs.existsSync('tmp/file1'), true); + +// simple - to file +shell.cp('resources/file2', 'tmp/file2'); +assert.equal(shell.error(), null); +assert.equal(fs.existsSync('tmp/file2'), true); + +// simple - file list +shell.rm('-rf', 'tmp/*'); +shell.cp('resources/file1', 'resources/file2', 'tmp'); +assert.equal(shell.error(), null); +assert.equal(fs.existsSync('tmp/file1'), true); +assert.equal(fs.existsSync('tmp/file2'), true); + +// simple - file list, array syntax +shell.rm('-rf', 'tmp/*'); +shell.cp(['resources/file1', 'resources/file2'], 'tmp'); +assert.equal(shell.error(), null); +assert.equal(fs.existsSync('tmp/file1'), true); +assert.equal(fs.existsSync('tmp/file2'), true); + +shell.cp('resources/file2', 'tmp/file3'); +assert.equal(fs.existsSync('tmp/file3'), true); +shell.cp('-f', 'resources/file2', 'tmp/file3'); // file exists, but -f specified +assert.equal(shell.error(), null); +assert.equal(fs.existsSync('tmp/file3'), true); + +// wildcard +shell.rm('tmp/file1', 'tmp/file2'); +shell.cp('resources/file*', 'tmp'); +assert.equal(shell.error(), null); +assert.equal(fs.existsSync('tmp/file1'), true); +assert.equal(fs.existsSync('tmp/file2'), true); + +//recursive, nothing exists +shell.rm('-rf', 'tmp/*'); +shell.cp('-R', 'resources/cp', 'tmp'); +assert.equal(shell.error(), null); +assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp/cp') + ''); + +//recursive, nothing exists, source ends in '/' (see Github issue #15) +shell.rm('-rf', 'tmp/*'); +shell.cp('-R', 'resources/cp/', 'tmp/'); +assert.equal(shell.error(), null); +assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp') + ''); + +//recursive, everything exists, no force flag +shell.rm('-rf', 'tmp/*'); +shell.cp('-R', 'resources/cp', 'tmp'); +shell.cp('-R', 'resources/cp', 'tmp'); +assert.equal(shell.error(), null); // crash test only + +//recursive, everything exists, with force flag +shell.rm('-rf', 'tmp/*'); +shell.cp('-R', 'resources/cp', 'tmp'); +'changing things around'.to('tmp/cp/dir_a/z'); +assert.notEqual(shell.cat('resources/cp/dir_a/z'), shell.cat('tmp/cp/dir_a/z')); // before cp +shell.cp('-Rf', 'resources/cp', 'tmp'); +assert.equal(shell.error(), null); +assert.equal(shell.cat('resources/cp/dir_a/z'), shell.cat('tmp/cp/dir_a/z')); // after cp + +//recursive, creates dest dir since it's only one level deep (see Github issue #44) +shell.rm('-rf', 'tmp/*'); +shell.cp('-r', 'resources/issue44/*', 'tmp/dir2'); +assert.equal(shell.error(), null); +assert.equal(shell.ls('-R', 'resources/issue44') + '', shell.ls('-R', 'tmp/dir2') + ''); +assert.equal(shell.cat('resources/issue44/main.js'), shell.cat('tmp/dir2/main.js')); + +//recursive, does *not* create dest dir since it's too deep (see Github issue #44) +shell.rm('-rf', 'tmp/*'); +shell.cp('-r', 'resources/issue44/*', 'tmp/dir2/dir3'); +assert.ok(shell.error()); +assert.equal(fs.existsSync('tmp/dir2'), false); + +shell.exit(123); http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/dirs.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/dirs.js b/node_modules/shelljs/test/dirs.js new file mode 100644 index 0000000..e9f11e6 --- /dev/null +++ b/node_modules/shelljs/test/dirs.js @@ -0,0 +1,37 @@ +var shell = require('..'); + +var assert = require('assert'), + path = require('path'), + fs = require('fs'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +shell.config.silent = true; + +var root = path.resolve(); + +shell.pushd('resources/pushd'); +shell.pushd('a'); + +var trail = [ + path.resolve(root, 'resources/pushd/a'), + path.resolve(root, 'resources/pushd'), + root +]; + +assert.deepEqual(shell.dirs(), trail); + +// Single items +assert.equal(shell.dirs('+0'), trail[0]); +assert.equal(shell.dirs('+1'), trail[1]); +assert.equal(shell.dirs('+2'), trail[2]); +assert.equal(shell.dirs('-0'), trail[2]); +assert.equal(shell.dirs('-1'), trail[1]); +assert.equal(shell.dirs('-2'), trail[0]); + +// Clearing items +assert.deepEqual(shell.dirs('-c'), []); +assert(!shell.error()); + +shell.exit(123); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/echo.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/echo.js b/node_modules/shelljs/test/echo.js new file mode 100644 index 0000000..82faa51 --- /dev/null +++ b/node_modules/shelljs/test/echo.js @@ -0,0 +1,50 @@ +var shell = require('..'); + +var assert = require('assert'), + path = require('path'), + fs = require('fs'), + child = require('child_process'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +shell.config.silent = true; + +function numLines(str) { + return typeof str === 'string' ? str.match(/\n/g).length : 0; +} + +shell.rm('-rf', 'tmp'); +shell.mkdir('tmp'); + +// +// Valids +// + + +// From here on we use child.exec() to intercept the stdout + + +// simple test with defaults +shell.mkdir('-p', 'tmp'); +var file = 'tmp/tempscript'+Math.random()+'.js', + script = 'require(\'../../global.js\'); echo("-asdf", "111");'; // test '-' bug (see issue #20) +script.to(file); +child.exec('node '+file, function(err, stdout, stderr) { + assert.ok(stdout === '-asdf 111\n' || stdout === '-asdf 111\nundefined\n'); // 'undefined' for v0.4 + + // simple test with silent(true) + shell.mkdir('-p', 'tmp'); + var file = 'tmp/tempscript'+Math.random()+'.js', + script = 'require(\'../../global.js\'); config.silent=true; echo(555);'; + script.to(file); + child.exec('node '+file, function(err, stdout, stderr) { + assert.ok(stdout === '555\n' || stdout === '555\nundefined\n'); // 'undefined' for v0.4 + + theEnd(); + }); +}); + +function theEnd() { + shell.exit(123); +} http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/env.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/env.js b/node_modules/shelljs/test/env.js new file mode 100644 index 0000000..0e041d6 --- /dev/null +++ b/node_modules/shelljs/test/env.js @@ -0,0 +1,19 @@ +var shell = require('..'); + +var assert = require('assert'); + +shell.config.silent = true; + +shell.rm('-rf', 'tmp'); +shell.mkdir('tmp'); + +// +// Valids +// + +assert.equal(shell.env['PATH'], process.env['PATH']); + +shell.env['SHELLJS_TEST'] = 'hello world'; +assert.equal(shell.env['SHELLJS_TEST'], process.env['SHELLJS_TEST']); + +shell.exit(123); http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/exec.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/exec.js b/node_modules/shelljs/test/exec.js new file mode 100644 index 0000000..e721808 --- /dev/null +++ b/node_modules/shelljs/test/exec.js @@ -0,0 +1,109 @@ +var shell = require('..'); + +var assert = require('assert'), + path = require('path'), + fs = require('fs'), + util = require('util'), + child = require('child_process'); + +shell.config.silent = true; + +function numLines(str) { + return typeof str === 'string' ? str.match(/\n/g).length : 0; +} + +// +// Invalids +// + +shell.exec(); +assert.ok(shell.error()); + +var result = shell.exec('asdfasdf'); // could not find command +assert.ok(result.code > 0); + + +// +// Valids +// + +// +// sync +// + +// check if stdout goes to output +var result = shell.exec('node -e \"console.log(1234);\"'); +assert.equal(shell.error(), null); +assert.equal(result.code, 0); +assert.ok(result.output === '1234\n' || result.output === '1234\nundefined\n'); // 'undefined' for v0.4 + +// check if stderr goes to output +var result = shell.exec('node -e \"console.error(1234);\"'); +assert.equal(shell.error(), null); +assert.equal(result.code, 0); +assert.ok(result.output === '1234\n' || result.output === '1234\nundefined\n'); // 'undefined' for v0.4 + +// check if stdout + stderr go to output +var result = shell.exec('node -e \"console.error(1234); console.log(666);\"'); +assert.equal(shell.error(), null); +assert.equal(result.code, 0); +assert.ok(result.output === '1234\n666\n' || result.output === '1234\n666\nundefined\n'); // 'undefined' for v0.4 + +// check exit code +var result = shell.exec('node -e \"process.exit(12);\"'); +assert.equal(shell.error(), null); +assert.equal(result.code, 12); + +// interaction with cd +shell.cd('resources/external'); +var result = shell.exec('node node_script.js'); +assert.equal(shell.error(), null); +assert.equal(result.code, 0); +assert.equal(result.output, 'node_script_1234\n'); +shell.cd('../..'); + +// check quotes escaping +var result = shell.exec( util.format('node -e "console.log(%s);"', "\\\"\\'+\\'_\\'+\\'\\\"") ); +assert.equal(shell.error(), null); +assert.equal(result.code, 0); +assert.equal(result.output, "'+'_'+'\n"); + +// +// async +// + +// no callback +var c = shell.exec('node -e \"console.log(1234)\"', {async:true}); +assert.equal(shell.error(), null); +assert.ok('stdout' in c, 'async exec returns child process object'); + +// +// callback as 2nd argument +// +shell.exec('node -e \"console.log(5678);\"', function(code, output) { + assert.equal(code, 0); + assert.ok(output === '5678\n' || output === '5678\nundefined\n'); // 'undefined' for v0.4 + + // + // callback as 3rd argument + // + shell.exec('node -e \"console.log(5566);\"', {async:true}, function(code, output) { + assert.equal(code, 0); + assert.ok(output === '5566\n' || output === '5566\nundefined\n'); // 'undefined' for v0.4 + + // + // callback as 3rd argument (slient:true) + // + shell.exec('node -e \"console.log(5678);\"', {silent:true}, function(code, output) { + assert.equal(code, 0); + assert.ok(output === '5678\n' || output === '5678\nundefined\n'); // 'undefined' for v0.4 + + shell.exit(123); + + }); + + }); + +}); + +assert.equal(shell.error(), null); http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/find.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/find.js b/node_modules/shelljs/test/find.js new file mode 100644 index 0000000..d375f86 --- /dev/null +++ b/node_modules/shelljs/test/find.js @@ -0,0 +1,56 @@ +var shell = require('..'); + +var assert = require('assert'), + path = require('path'), + fs = require('fs'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +shell.config.silent = true; + +shell.rm('-rf', 'tmp'); +shell.mkdir('tmp'); + +// +// Invalids +// + +var result = shell.find(); // no paths given +assert.ok(shell.error()); + +// +// Valids +// + +// current path +shell.cd('resources/find'); +var result = shell.find('.'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('.hidden') > -1, true); +assert.equal(result.indexOf('dir1/dir11/a_dir11') > -1, true); +assert.equal(result.length, 11); +shell.cd('../..'); + +// simple path +var result = shell.find('resources/find'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('resources/find/.hidden') > -1, true); +assert.equal(result.indexOf('resources/find/dir1/dir11/a_dir11') > -1, true); +assert.equal(result.length, 11); + +// multiple paths - comma +var result = shell.find('resources/find/dir1', 'resources/find/dir2'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('resources/find/dir1/dir11/a_dir11') > -1, true); +assert.equal(result.indexOf('resources/find/dir2/a_dir1') > -1, true); +assert.equal(result.length, 6); + +// multiple paths - array +var result = shell.find(['resources/find/dir1', 'resources/find/dir2']); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('resources/find/dir1/dir11/a_dir11') > -1, true); +assert.equal(result.indexOf('resources/find/dir2/a_dir1') > -1, true); +assert.equal(result.length, 6); + +shell.exit(123); http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/grep.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/grep.js b/node_modules/shelljs/test/grep.js new file mode 100644 index 0000000..71db982 --- /dev/null +++ b/node_modules/shelljs/test/grep.js @@ -0,0 +1,59 @@ +var shell = require('..'); + +var assert = require('assert'), + path = require('path'), + fs = require('fs'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +shell.config.silent = true; + +function numLines(str) { + return typeof str === 'string' ? str.match(/\n/g).length : 0; +} + +shell.rm('-rf', 'tmp'); +shell.mkdir('tmp'); + +// +// Invalids +// + +shell.grep(); +assert.ok(shell.error()); + +shell.grep(/asdf/g); // too few args +assert.ok(shell.error()); + +assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check +shell.grep(/asdf/g, '/asdfasdf'); // no such file +assert.ok(shell.error()); + +// +// Valids +// + +var result = shell.grep('line', 'resources/a.txt'); +assert.equal(shell.error(), null); +assert.equal(result.split('\n').length - 1, 4); + +var result = shell.grep('-v', 'line', 'resources/a.txt'); +assert.equal(shell.error(), null); +assert.equal(result.split('\n').length - 1, 8); + +var result = shell.grep('line one', 'resources/a.txt'); +assert.equal(shell.error(), null); +assert.equal(result, 'This is line one\n'); + +// multiple files +var result = shell.grep(/test/, 'resources/file1.txt', 'resources/file2.txt'); +assert.equal(shell.error(), null); +assert.equal(result, 'test1\ntest2\n'); + +// multiple files, array syntax +var result = shell.grep(/test/, ['resources/file1.txt', 'resources/file2.txt']); +assert.equal(shell.error(), null); +assert.equal(result, 'test1\ntest2\n'); + +shell.exit(123); http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/ls.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/ls.js b/node_modules/shelljs/test/ls.js new file mode 100644 index 0000000..5067b7d --- /dev/null +++ b/node_modules/shelljs/test/ls.js @@ -0,0 +1,202 @@ +var shell = require('..'); + +var assert = require('assert'), + path = require('path'), + fs = require('fs'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +shell.config.silent = true; + +function numLines(str) { + return typeof str === 'string' ? str.match(/\n/g).length : 0; +} + +shell.rm('-rf', 'tmp'); +shell.mkdir('tmp'); + +// +// Invalids +// + +assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check +var result = shell.ls('/asdfasdf'); // no such file or dir +assert.ok(shell.error()); +assert.equal(result.length, 0); + +// +// Valids +// + +var result = shell.ls(); +assert.equal(shell.error(), null); + +var result = shell.ls('/'); +assert.equal(shell.error(), null); + +// no args +shell.cd('resources/ls'); +var result = shell.ls(); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('file1') > -1, true); +assert.equal(result.indexOf('file2') > -1, true); +assert.equal(result.indexOf('file1.js') > -1, true); +assert.equal(result.indexOf('file2.js') > -1, true); +assert.equal(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1, true); +assert.equal(result.indexOf('a_dir') > -1, true); +assert.equal(result.length, 6); +shell.cd('../..'); + +// simple arg +var result = shell.ls('resources/ls'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('file1') > -1, true); +assert.equal(result.indexOf('file2') > -1, true); +assert.equal(result.indexOf('file1.js') > -1, true); +assert.equal(result.indexOf('file2.js') > -1, true); +assert.equal(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1, true); +assert.equal(result.indexOf('a_dir') > -1, true); +assert.equal(result.length, 6); + +// no args, 'all' option +shell.cd('resources/ls'); +var result = shell.ls('-A'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('file1') > -1, true); +assert.equal(result.indexOf('file2') > -1, true); +assert.equal(result.indexOf('file1.js') > -1, true); +assert.equal(result.indexOf('file2.js') > -1, true); +assert.equal(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1, true); +assert.equal(result.indexOf('a_dir') > -1, true); +assert.equal(result.indexOf('.hidden_file') > -1, true); +assert.equal(result.indexOf('.hidden_dir') > -1, true); +assert.equal(result.length, 8); +shell.cd('../..'); + +// no args, 'all' option +shell.cd('resources/ls'); +var result = shell.ls('-a'); // (deprecated) backwards compatibility test +assert.equal(shell.error(), null); +assert.equal(result.indexOf('file1') > -1, true); +assert.equal(result.indexOf('file2') > -1, true); +assert.equal(result.indexOf('file1.js') > -1, true); +assert.equal(result.indexOf('file2.js') > -1, true); +assert.equal(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1, true); +assert.equal(result.indexOf('a_dir') > -1, true); +assert.equal(result.indexOf('.hidden_file') > -1, true); +assert.equal(result.indexOf('.hidden_dir') > -1, true); +assert.equal(result.length, 8); +shell.cd('../..'); + +// wildcard, simple +var result = shell.ls('resources/ls/*'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('resources/ls/file1') > -1, true); +assert.equal(result.indexOf('resources/ls/file2') > -1, true); +assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); +assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); +assert.equal(result.indexOf('resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1, true); +assert.equal(result.indexOf('resources/ls/a_dir') > -1, true); +assert.equal(result.length, 6); + +// wildcard, hidden only +var result = shell.ls('resources/ls/.*'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('resources/ls/.hidden_file') > -1, true); +assert.equal(result.indexOf('resources/ls/.hidden_dir') > -1, true); +assert.equal(result.length, 2); + +// wildcard, mid-file +var result = shell.ls('resources/ls/f*le*'); +assert.equal(shell.error(), null); +assert.equal(result.length, 5); +assert.equal(result.indexOf('resources/ls/file1') > -1, true); +assert.equal(result.indexOf('resources/ls/file2') > -1, true); +assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); +assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); +assert.equal(result.indexOf('resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1, true); + +// wildcard, mid-file with dot (should escape dot for regex) +var result = shell.ls('resources/ls/f*le*.js'); +assert.equal(shell.error(), null); +assert.equal(result.length, 2); +assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); +assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); + +// wildcard, should not do partial matches +var result = shell.ls('resources/ls/*.j'); // shouldn't get .js +assert.equal(shell.error(), null); +assert.equal(result.length, 0); + +// wildcard, all files with extension +var result = shell.ls('resources/ls/*.*'); +assert.equal(shell.error(), null); +assert.equal(result.length, 3); +assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); +assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); +assert.equal(result.indexOf('resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1, true); + +// wildcard, with additional path +var result = shell.ls('resources/ls/f*le*.js', 'resources/ls/a_dir'); +assert.equal(shell.error(), null); +assert.equal(result.length, 4); +assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); +assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); +assert.equal(result.indexOf('b_dir') > -1, true); // no wildcard == no path prefix +assert.equal(result.indexOf('nada') > -1, true); // no wildcard == no path prefix + +// wildcard for both paths +var result = shell.ls('resources/ls/f*le*.js', 'resources/ls/a_dir/*'); +assert.equal(shell.error(), null); +assert.equal(result.length, 4); +assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); +assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); +assert.equal(result.indexOf('resources/ls/a_dir/b_dir') > -1, true); +assert.equal(result.indexOf('resources/ls/a_dir/nada') > -1, true); + +// wildcard for both paths, array +var result = shell.ls(['resources/ls/f*le*.js', 'resources/ls/a_dir/*']); +assert.equal(shell.error(), null); +assert.equal(result.length, 4); +assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); +assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); +assert.equal(result.indexOf('resources/ls/a_dir/b_dir') > -1, true); +assert.equal(result.indexOf('resources/ls/a_dir/nada') > -1, true); + +// recursive, no path +shell.cd('resources/ls'); +var result = shell.ls('-R'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('a_dir') > -1, true); +assert.equal(result.indexOf('a_dir/b_dir') > -1, true); +assert.equal(result.indexOf('a_dir/b_dir/z') > -1, true); +assert.equal(result.length, 9); +shell.cd('../..'); + +// recusive, path given +var result = shell.ls('-R', 'resources/ls'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('a_dir') > -1, true); +assert.equal(result.indexOf('a_dir/b_dir') > -1, true); +assert.equal(result.indexOf('a_dir/b_dir/z') > -1, true); +assert.equal(result.length, 9); + +// recusive, path given - 'all' flag +var result = shell.ls('-RA', 'resources/ls'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('a_dir') > -1, true); +assert.equal(result.indexOf('a_dir/b_dir') > -1, true); +assert.equal(result.indexOf('a_dir/b_dir/z') > -1, true); +assert.equal(result.indexOf('a_dir/.hidden_dir/nada') > -1, true); +assert.equal(result.length, 14); + +// recursive, wildcard +var result = shell.ls('-R', 'resources/ls/*'); +assert.equal(shell.error(), null); +assert.equal(result.indexOf('resources/ls/a_dir') > -1, true); +assert.equal(result.indexOf('resources/ls/a_dir/b_dir') > -1, true); +assert.equal(result.indexOf('resources/ls/a_dir/b_dir/z') > -1, true); +assert.equal(result.length, 9); + +shell.exit(123); http://git-wip-us.apache.org/repos/asf/cordova-browser/blob/926681b8/node_modules/shelljs/test/make.js ---------------------------------------------------------------------- diff --git a/node_modules/shelljs/test/make.js b/node_modules/shelljs/test/make.js new file mode 100644 index 0000000..3edbd8c --- /dev/null +++ b/node_modules/shelljs/test/make.js @@ -0,0 +1,20 @@ +var shell = require('..'), + child = require('child_process'), + assert = require('assert'); + +shell.mkdir('-p', 'tmp'); +var file = 'tmp/tempscript'+Math.random()+'.js', + script = 'require(\'../../make.js\');' + + 'target.all=function(){' + + ' echo("first"); '+ + ' cp("this_file_doesnt_exist", ".");' + + ' echo("second");' + + '}'; + +script.to(file); +child.exec('node '+file, function(err, stdout, stderr) { + assert.ok(stdout.match('first')); + assert.ok(!stdout.match('second')); // Make should die on errors, so this should never get echoed + + shell.exit(123); +});