Skip to content

Commit

Permalink
Improved OS.parse()
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Robinson committed Mar 3, 2010
1 parent 0ea83b4 commit 106140a
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 63 deletions.
134 changes: 75 additions & 59 deletions lib/os.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,73 +89,89 @@ exports.enquote = function (word) {
* This utility is used by Narwhal to pare arguments from
* system.env.NARWHAL_OPT.
*/
exports.parse = function (args) {
var startsWith = function(self, str) {
return (self.match("^"+str.replace('\\', '\\\\'))==str)
}
var endsWith = function(self, str) {
return (self.match(str.replace('\\', '\\\\')+"$")==str)
}
var results = [], quoteType = null, quotedElement = null;
args.split(' ').forEach(function(element) {
if (
quoteType || endsWith(element, '\\') ||
startsWith(element, '\'') || startsWith(element, '"')
) {
if (quoteType) {
if (endsWith(element, quoteType)) {
results.push(
quotedElement + " " +
element.substring(0, element.length - 1)
);
quotedElement = null;
quoteType = null;
return;

var STATE_NORMAL = 0; // waiting for non whitespace/quote
var STATE_ARG = 1; // nextArg is an argument, even if empty
var STATE_IN_QUOTE = 2; // within a ' or " quote

exports.parse = function (argString) {
var args = [];

var nextArg = "";
var state = STATE_NORMAL;
var escapeNext = false;
var delimiter;

var tokens = argString.split("");
while (tokens.length > 0) {
var token = tokens.shift();

if (state === STATE_NORMAL || state === STATE_ARG) {
if (!escapeNext && token === "\\") {
escapeNext = true;
}
else { quotedElement += " " + element; return; }
}
if (!quoteType && startsWith(element, '\'')) {
quoteType = '\'';
quotedElement = element.substring(1, element.length);
return;
}
if (!quoteType && startsWith(element, '"')) {
quoteType = '"';
quotedElement = element.substring(1, element.length);
return;
else if (escapeNext) {
state = STATE_ARG;
escapeNext = false;
nextArg += token;
}
if (!quoteType) {
if (endsWith(element, '\\')) {
if (quotedElement) {
quotedElement += " " + element.substring(
0,
element.length - 1
);
}
else {
quotedElement = element.substring(
0,
element.length - 1
);
}
}
else {
results.push(quotedElement + ' ' + element);
quotedElement = null;
return;
else if (token === "'" || token === '"') {
delimiter = token;
state = STATE_IN_QUOTE;
}
else if (token === " ") {
if (state === STATE_ARG) {
args.push(nextArg);
nextArg = "";
}
state = STATE_NORMAL;
}
else {
nextArg += token;
state = STATE_ARG;
}
}
else {
if (quotedElement) {
results.push(quotedElement + ' ' + element);
quotedElement = null;
else if (state === STATE_IN_QUOTE) {
if (!escapeNext && token === "\\") {
escapeNext = true;
}
else if (delimiter === token) {
if (escapeNext) {
nextArg += token;
escapeNext = false;
} else {
state = STATE_ARG;
}
}
else {
results.push(element);
if (escapeNext) {
// if not a quote (above) or other special character that needs to be escaped then include the backslash
if (token !== "\\")
nextArg += "\\";
nextArg += token;
escapeNext = false;
} else {
nextArg += token;
}
}
}
});
return results;
else {
throw "wtf " + state;
}
}

if (state === STATE_IN_QUOTE) {
if (token === delimiter) {
args.push(nextArg.slice(0,-1) + "\\");
}
else {
// throw "Invalid or not yet implemented case"
}
}
else if (state === STATE_ARG) {
args.push(nextArg);
}

return args;
};

13 changes: 9 additions & 4 deletions tests/os/parse-fuzzer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ var ASSERT = require("assert");
var UTIL = require("UTIL");
var stream = require("term").stream;

// config
var random = false;
var tokens = ["x", "'", '"', " "];
var maxLength = 10;
var tokens = ["x", "'", '"', " ", "\\"];

function shellParse(argString) {
// print(argString)
var p = OS.popen(["sh", "-c", 'for i in "$@"; do echo "$i"; done', "--"].map(OS.enquote).join(" ") + " " + argString);
var result = p.stdout.read().split("\n").slice(0,-1);
var x = p.stdout.read();
// print("["+x+"]")
var result = x.split("\n").slice(0,-1);
if (p.wait())
throw result;
return result;
Expand All @@ -19,7 +24,7 @@ function buildTestString() {

if (random) {
var components = [];
var n = Math.round(Math.random()*10);
var n = Math.round(Math.random()*maxLength);
while (components.length < n)
components.push(tokens[Math.floor(Math.random()*tokens.length)]);
return components.join("");
Expand Down Expand Up @@ -50,5 +55,5 @@ while (true) {
var pass = UTIL.eq(expectedArgs, actualArgs);

stream.print("[" + testIndex + "] " + (pass ? "\0green(PASS\0)" : "\0red(FAIL\0)") +
": string=["+argString+"] expected=["+expectedArgs+"] actual=["+actualArgs+"]");
": string=["+argString+"] expected=["+expectedArgs+"]("+expectedArgs.length+") actual=["+actualArgs+"]("+actualArgs.length+")");
}

0 comments on commit 106140a

Please sign in to comment.