Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Improved OS.parse()

  • Loading branch information...
commit 106140a1dd37d09f3ce14d98ed168b527bdb64b6 1 parent 0ea83b4
Tom Robinson authored
Showing with 84 additions and 63 deletions.
  1. +75 −59 lib/os.js
  2. +9 −4 tests/os/parse-fuzzer.js
View
134 lib/os.js
@@ -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;
};
View
13 tests/os/parse-fuzzer.js
@@ -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;
@@ -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("");
@@ -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+")");
}
Please sign in to comment.
Something went wrong with that request. Please try again.