shell-args
Version:
Parsing and quoting for shell command lines that supports both bash and windows styles of quoting.
228 lines • 7.13 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
function bashShellParse(cmdLine) {
let State;
(function (State) {
State[State["Base"] = 0] = "Base";
State[State["Normal"] = 1] = "Normal";
State[State["Single"] = 2] = "Single";
State[State["Double"] = 3] = "Double";
})(State || (State = {}));
let results = [];
let current = "";
let blockStart = 0;
let pos = 0;
let state = State.Base;
// Add the current block to the argument.
function addBlock() {
current += cmdLine.substring(blockStart, pos);
blockStart = pos + 1;
}
// Pushes the current argument to the results.
function push() {
// Add any pending block.
addBlock();
// This might fail at the end of the command line.
if (current.length) {
results.push(current);
}
// Onto the next argument.
current = "";
}
// What is it escaping?
function escaped() {
if (state === State.Single || state === State.Base || pos + 1 >= cmdLine.length) {
return undefined;
}
let next = cmdLine.charAt(pos + 1);
if (state === State.Double) {
// In double quotes only some things can be escaped.
if (next === "\"" || next === "\\" || next === "\n") {
return next;
}
return undefined;
}
return next;
}
while (pos < cmdLine.length) {
let char = cmdLine.charAt(pos);
let escape = char === "\\" ? escaped() : undefined;
if (escape) {
// Cache up to here.
addBlock();
if (escape === "\n") {
// Entirely skip the newline.
blockStart++;
}
// Skip over the escaped character.
pos += 2;
continue;
}
switch (state) {
case State.Base: {
// Still not in an argument?
if (char === " " || char === "\t" || char === "\n") {
pos++;
continue;
}
// The new argument starts here.
blockStart = pos;
state = State.Normal;
// Recheck states using the same character.
continue;
break;
}
case State.Normal: {
if (char === " " || char === "\t" || char === "\n") {
// Found the end of the argument.
push();
state = State.Base;
}
else if (char === "'") {
// Start of a single-quoted block.
addBlock();
state = State.Single;
}
else if (char === "\"") {
// Start of a double-quoted block.
addBlock();
state = State.Double;
}
// Just a regular character to be included in the block.
break;
}
case State.Single: {
if (char === "'") {
// End of the block.
addBlock();
state = State.Normal;
}
break;
}
case State.Double: {
if (char === "\"") {
// End of the block.
addBlock();
state = State.Normal;
}
break;
}
}
pos++;
}
if (state !== State.Base) {
push();
}
return results;
}
exports.bashShellParse = bashShellParse;
function winShellParse(cmdLine) {
let State;
(function (State) {
State[State["Base"] = 0] = "Base";
State[State["Normal"] = 1] = "Normal";
State[State["Double"] = 2] = "Double";
})(State || (State = {}));
let results = [];
let current = "";
let blockStart = 0;
let pos = 0;
let state = State.Base;
let escapeCount = 0;
// Add the current block to the argument.
function addBlock() {
current += cmdLine.substring(blockStart, pos);
blockStart = pos + 1;
}
// Adds any pending escape characters.
function addEscapes() {
current += "\\".repeat(escapeCount);
escapeCount = 0;
}
// Pushes the current argument to the results.
function push() {
// This might fail at the end of the command line.
if (current.length) {
results.push(current);
}
// Onto the next argument.
current = "";
}
while (pos < cmdLine.length) {
let char = cmdLine.charAt(pos);
if (char === "\\") {
if (escapeCount === 0) {
if (state !== State.Base) {
addBlock();
}
else {
state = State.Normal;
}
}
escapeCount++;
}
else if (char === "\"") {
if (escapeCount % 2) {
// An escaped double quote.
escapeCount = (escapeCount - 1) / 2;
addEscapes();
// Include the quote in the next block and start processing with the following character.
blockStart = pos;
}
else {
if (escapeCount) {
// An unescaped double quote.
escapeCount /= 2;
addEscapes();
}
else if (state !== State.Base) {
addBlock();
}
// Next block starts after the quote, move into the right state and start processing
// with the next character.
blockStart = pos + 1;
if (state === State.Double) {
state = State.Normal;
}
else {
state = State.Double;
}
}
}
else {
if (escapeCount) {
addEscapes();
blockStart = pos;
}
if (char === " " || char === "\t") {
if (state === State.Normal) {
// Found the end of the argument.
addBlock();
push();
state = State.Base;
}
}
else if (state === State.Base) {
// Found the start of the new argument.
state = State.Normal;
blockStart = pos;
}
}
pos++;
}
if (state !== State.Base) {
addEscapes();
addBlock();
push();
}
return results;
}
exports.winShellParse = winShellParse;
function shellParse(cmdLine) {
if (process.platform === "win32") {
return winShellParse(cmdLine);
}
return bashShellParse(cmdLine);
}
exports.shellParse = shellParse;
//# sourceMappingURL=parse.js.map