Skip to content

Commit

Permalink
Fixes #137: Follow Windows conventions when composing cmdline
Browse files Browse the repository at this point in the history
  • Loading branch information
the-ress authored and Tyriar committed Oct 12, 2016
1 parent 374dde0 commit ed3c9da
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 3 deletions.
54 changes: 52 additions & 2 deletions lib/pty_win.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,15 @@ function Agent(file, args, env, cwd, cols, rows, debug) {

// Sanitize input variable.
file = file;
args = args.join(' ');
cwd = path.resolve(cwd);

// Compose command line
var cmdline = [file];
Array.prototype.push.apply(cmdline, args);
cmdline = argvToCommandLine(cmdline);

// Start terminal session.
pty.startProcess(self.pid, file, args, env, cwd);
pty.startProcess(self.pid, file, cmdline, env, cwd);

// Emit ready event.
self.ptySocket.emit('ready_datapipe', socket);
Expand Down Expand Up @@ -357,10 +361,56 @@ function environ(env) {
return pairs;
}

// Convert argc/argv into a Win32 command-line following the escaping convention
// documented on MSDN. (e.g. see CommandLineToArgvW documentation)
// Copied from winpty project.
function argvToCommandLine(argv) {
var result = '';
for (var argIndex = 0; argIndex < argv.length; argIndex++) {
if (argIndex > 0) {
result += ' ';
}
var arg = argv[argIndex];
var quote =
arg.indexOf(' ') != -1 ||
arg.indexOf('\t') != -1 ||
arg == '';
if (quote) {
result += '\"';
}
var bsCount = 0;
for (var i = 0; i < arg.length; i++) {
var p = arg[i];
if (p == '\\') {
bsCount++;
} else if (p == '"') {
result += '\\'.repeat(bsCount * 2 + 1);
result += '"';
bsCount = 0;
} else {
result += '\\'.repeat(bsCount);
bsCount = 0;
result += p;
}
}
if (quote) {
result += '\\'.repeat(bsCount * 2);
result += '\"';
} else {
result += '\\'.repeat(bsCount);
}
}
return result;
}

/**
* Expose
*/

module.exports = exports = Terminal;
exports.Terminal = Terminal;
exports.native = pty;

if (process.env.NODE_ENV == 'test') {
exports.argvToCommandLine = argvToCommandLine;
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"scripts": {
"install": "node scripts/install.js",
"postinstall": "node scripts/post-install.js",
"test": "NODE_ENV=test mocha -R spec"
"test": "NODE_ENV=test mocha -R spec",
"test-windows": "set \"NODE_ENV=test\" && mocha -R spec test-windows"
},
"tags": [
"pty",
Expand Down
59 changes: 59 additions & 0 deletions test-windows/argvToCommandLine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
var argvToCommandLine = require('../').argvToCommandLine
var assert = require("assert");

function check(input, expected) {
assert.equal(argvToCommandLine(input), expected);
}

describe("argvToCommandLine", function() {
describe("Plain strings", function() {
it("doesn't quote plain string", function() {
check(['asdf'], 'asdf');
});
it("doesn't escape backslashes", function() {
check(['\\asdf\\qwer\\'], '\\asdf\\qwer\\');
});
it("doesn't escape multiple backslashes", function() {
check(['asdf\\\\qwer'], 'asdf\\\\qwer');
});
it("adds backslashes before quotes", function() {
check(['"asdf"qwer"'], '\\"asdf\\"qwer\\"');
});
it("escapes backslashes before quotes", function() {
check(['asdf\\"qwer'], 'asdf\\\\\\"qwer');
});
});

describe("Quoted strings", function() {
it("quotes string with spaces", function() {
check(['asdf qwer'], '"asdf qwer"');
});
it("quotes empty string", function() {
check([''], '""');
});
it("quotes string with tabs", function() {
check(['asdf\tqwer'], '"asdf\tqwer"');
});
it("escapes only the last backslash", function() {
check(['\\asdf \\qwer\\'], '"\\asdf \\qwer\\\\"');
});
it("doesn't escape multiple backslashes", function() {
check(['asdf \\\\qwer'], '"asdf \\\\qwer"');
});
it("adds backslashes before quotes", function() {
check(['"asdf "qwer"'], '"\\"asdf \\"qwer\\""');
});
it("escapes backslashes before quotes", function() {
check(['asdf \\"qwer'], '"asdf \\\\\\"qwer"');
});
it("escapes multiple backslashes at the end", function() {
check(['asdf qwer\\\\'], '"asdf qwer\\\\\\\\"');
});
});

describe("Multiple arguments", function() {
it("joins arguments with spaces", function() {
check(['asdf', 'qwer zxcv', '', '"'], 'asdf "qwer zxcv" "" \\"');
});
});
});

0 comments on commit ed3c9da

Please sign in to comment.