Skip to content

Added a workaround for incorrect double quote handling on Windows. #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 138 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,23 @@
'use strict';
var spawn = require('child_process').spawn;

var sh, shFlag, children, args, wait, cmds, verbose, i ,len;
var sh, shFlag, children, originalArgs, args, hasFixedArgs, wait, cmds, verbose, i, len;
// parsing argv
cmds = [];
args = process.argv.slice(2);
args = originalArgs = process.argv.slice(2);

// cross platform compatibility
if (process.platform === 'win32') {
sh = 'cmd';
shFlag = '/c';

args = fixArgsOnWindows(originalArgs);
hasFixedArgs = true;
} else {
sh = 'sh';
shFlag = '-c';
}

for (i = 0, len = args.length; i < len; i++) {
if (args[i][0] === '-') {
switch (args[i]) {
Expand All @@ -31,6 +44,15 @@ for (i = 0, len = args.length; i < len; i++) {
}
}

if (verbose && hasFixedArgs) {
console.log('### Original arguments ###');
console.log(originalArgs.join('\n'));
console.log('\n');
console.log('### Fixed arguments ###');
console.log(args.join('\n'));
console.log('\n');
}

// called on close of a child process
function childClose (code) {
var i, len;
Expand Down Expand Up @@ -77,15 +99,6 @@ function close (code) {
process.exit(code);
}

// cross platform compatibility
if (process.platform === 'win32') {
sh = 'cmd';
shFlag = '/c';
} else {
sh = 'sh';
shFlag = '-c';
}

// start the children
children = [];
cmds.forEach(function (cmd) {
Expand All @@ -101,3 +114,117 @@ cmds.forEach(function (cmd) {

// close all children on ctrl+c
process.on('SIGINT', close)



/*
* START Workaround for incorrect double quote handling on Windows:
* https://github.com/joyent/node/issues/25339
*/

function fixArgsOnWindows (args) {
// Detect faulty arguments, return if not found
if (!hasFaultyArg(args)) return args;

var notQuoteOrSpaceAtEndRe = /[^\s"]+$/;
var whitespaceRe = /\s+/g;
var argsJoined = args.join(' ');
var level = -1;
var quotesForLevel = getQuotesForLevel(level);
var quotesForNextLevel = getQuotesForLevel(level + 1);
var index = 0;
var levelQuoteIndex = -1;
var nextLevelQuoteIndex = -1;
var result = [];
var argsSlice;

while (true) {
// Iterate on quote occurrences

levelQuoteIndex = quotesForLevel ? argsJoined.indexOf(quotesForLevel, index) : -1;
nextLevelQuoteIndex = argsJoined.indexOf(quotesForNextLevel, index);

if (nextLevelQuoteIndex > -1 && nextLevelQuoteIndex == levelQuoteIndex) {
// Level is being increased
argsSlice = argsJoined.slice(index, nextLevelQuoteIndex);
if (level == -1) {
// Level was root level
// Root level arguments are split on space
result = result.concat(argsSlice.split(whitespaceRe));
// Only start a new argument when coming from root level
result.push(quotesForNextLevel);
} else {
// Level was deeper than root: append to last item
if (result[result.length - 1].match(notQuoteOrSpaceAtEndRe)) result[result.length - 1] += ' ';
result[result.length - 1] += argsSlice + quotesForNextLevel;
}
index = nextLevelQuoteIndex + quotesForNextLevel.length;
// Increase level
level++;
quotesForLevel = quotesForNextLevel;
quotesForNextLevel = getQuotesForLevel(level + 1);
} else if (levelQuoteIndex > -1) {
// Level is being decreased: can only happen when not root
argsSlice = argsJoined.slice(index, levelQuoteIndex + quotesForLevel.length);
if (result[result.length - 1].match(notQuoteOrSpaceAtEndRe)) result[result.length - 1] += ' ';
result[result.length - 1] += argsSlice;
index = levelQuoteIndex + quotesForLevel.length;
// Decrease level
level--;
quotesForNextLevel = quotesForLevel;
quotesForLevel = getQuotesForLevel(level);
} else {
// This is the last slice
argsSlice = argsJoined.slice(index);
if (level == -1) {
result = result.concat(argsSlice.split(whitespaceRe));
break;
} else {
throw 'parallelshell: Invalid quote nesting.';
}
}
};

for (var i = result.length - 1; i >= 0; i--) {
var arg = result[i].replace(/"+/g, function(match) {
// Remove one level of quotes
if (match.length == 1) {
return '';
} else {
return match.slice(0, match.length / 2);
}
});
if (arg) {
result[i] = arg;
} else {
// Remove empty arguments
result.splice(i, 1);
}
}

return result;
}

function hasFaultyArg (args) {
var re = /^\s*".*[^"]\s*$/;
for (var i = 0, n = args.length; i < n; i++) {
var arg = args[i];
if (re.test(arg)) {
return true;
}
}
return false;
}

function getQuotesForLevel (level) {
var count = Math.pow(2, level);
var result = '';
for (var i = 0; i < count; i++) {
result += '"';
}
return result;
}

/*
* END Workaround
*/