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);
+});

Reply via email to