114 lines
3.8 KiB
JavaScript
114 lines
3.8 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
var resolveCommand = require('./util/resolveCommand');
|
||
|
var hasEmptyArgumentBug = require('./util/hasEmptyArgumentBug');
|
||
|
var escapeArgument = require('./util/escapeArgument');
|
||
|
var escapeCommand = require('./util/escapeCommand');
|
||
|
var readShebang = require('./util/readShebang');
|
||
|
|
||
|
var isWin = process.platform === 'win32';
|
||
|
var skipShellRegExp = /\.(?:com|exe)$/i;
|
||
|
|
||
|
// Supported in Node >= 6 and >= 4.8
|
||
|
var supportsShellOption = parseInt(process.version.substr(1).split('.')[0], 10) >= 6 ||
|
||
|
parseInt(process.version.substr(1).split('.')[0], 10) === 4 && parseInt(process.version.substr(1).split('.')[1], 10) >= 8;
|
||
|
|
||
|
function parseNonShell(parsed) {
|
||
|
var shebang;
|
||
|
var needsShell;
|
||
|
var applyQuotes;
|
||
|
|
||
|
if (!isWin) {
|
||
|
return parsed;
|
||
|
}
|
||
|
|
||
|
// Detect & add support for shebangs
|
||
|
parsed.file = resolveCommand(parsed.command);
|
||
|
parsed.file = parsed.file || resolveCommand(parsed.command, true);
|
||
|
shebang = parsed.file && readShebang(parsed.file);
|
||
|
|
||
|
if (shebang) {
|
||
|
parsed.args.unshift(parsed.file);
|
||
|
parsed.command = shebang;
|
||
|
needsShell = hasEmptyArgumentBug || !skipShellRegExp.test(resolveCommand(shebang) || resolveCommand(shebang, true));
|
||
|
} else {
|
||
|
needsShell = hasEmptyArgumentBug || !skipShellRegExp.test(parsed.file);
|
||
|
}
|
||
|
|
||
|
// If a shell is required, use cmd.exe and take care of escaping everything correctly
|
||
|
if (needsShell) {
|
||
|
// Escape command & arguments
|
||
|
applyQuotes = (parsed.command !== 'echo'); // Do not quote arguments for the special "echo" command
|
||
|
parsed.command = escapeCommand(parsed.command);
|
||
|
parsed.args = parsed.args.map(function (arg) {
|
||
|
return escapeArgument(arg, applyQuotes);
|
||
|
});
|
||
|
|
||
|
// Make use of cmd.exe
|
||
|
parsed.args = ['/d', '/s', '/c', '"' + parsed.command + (parsed.args.length ? ' ' + parsed.args.join(' ') : '') + '"'];
|
||
|
parsed.command = process.env.comspec || 'cmd.exe';
|
||
|
parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
|
||
|
}
|
||
|
|
||
|
return parsed;
|
||
|
}
|
||
|
|
||
|
function parseShell(parsed) {
|
||
|
var shellCommand;
|
||
|
|
||
|
// If node supports the shell option, there's no need to mimic its behavior
|
||
|
if (supportsShellOption) {
|
||
|
return parsed;
|
||
|
}
|
||
|
|
||
|
// Mimic node shell option, see: https://github.com/nodejs/node/blob/b9f6a2dc059a1062776133f3d4fd848c4da7d150/lib/child_process.js#L335
|
||
|
shellCommand = [parsed.command].concat(parsed.args).join(' ');
|
||
|
|
||
|
if (isWin) {
|
||
|
parsed.command = typeof parsed.options.shell === 'string' ? parsed.options.shell : process.env.comspec || 'cmd.exe';
|
||
|
parsed.args = ['/d', '/s', '/c', '"' + shellCommand + '"'];
|
||
|
parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
|
||
|
} else {
|
||
|
if (typeof parsed.options.shell === 'string') {
|
||
|
parsed.command = parsed.options.shell;
|
||
|
} else if (process.platform === 'android') {
|
||
|
parsed.command = '/system/bin/sh';
|
||
|
} else {
|
||
|
parsed.command = '/bin/sh';
|
||
|
}
|
||
|
|
||
|
parsed.args = ['-c', shellCommand];
|
||
|
}
|
||
|
|
||
|
return parsed;
|
||
|
}
|
||
|
|
||
|
// ------------------------------------------------
|
||
|
|
||
|
function parse(command, args, options) {
|
||
|
var parsed;
|
||
|
|
||
|
// Normalize arguments, similar to nodejs
|
||
|
if (args && !Array.isArray(args)) {
|
||
|
options = args;
|
||
|
args = null;
|
||
|
}
|
||
|
|
||
|
args = args ? args.slice(0) : []; // Clone array to avoid changing the original
|
||
|
options = options || {};
|
||
|
|
||
|
// Build our parsed object
|
||
|
parsed = {
|
||
|
command: command,
|
||
|
args: args,
|
||
|
options: options,
|
||
|
file: undefined,
|
||
|
original: command,
|
||
|
};
|
||
|
|
||
|
// Delegate further parsing to shell or non-shell
|
||
|
return options.shell ? parseShell(parsed) : parseNonShell(parsed);
|
||
|
}
|
||
|
|
||
|
module.exports = parse;
|