8049300: jjs scripting: need way to quote $EXEC command arguments to protect spaces

Honor quoting with "" and '' as well as escaped spaces

Reviewed-by: hannesw, sundar
This commit is contained in:
Michael Haupt 2015-05-15 16:36:25 +02:00
parent c9191f56e0
commit ff3855c8ba

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -34,10 +34,13 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Global functions supported only in scripting mode.
@ -133,15 +136,8 @@ public final class ScriptingFunctions {
// Current global is need to fetch additional inputs and for additional results.
final ScriptObject global = Context.getGlobal();
// Break exec string into tokens.
final StringTokenizer tokenizer = new StringTokenizer(JSType.toString(string));
final String[] cmdArray = new String[tokenizer.countTokens()];
for (int i = 0; tokenizer.hasMoreTokens(); i++) {
cmdArray[i] = tokenizer.nextToken();
}
// Set up initial process.
final ProcessBuilder processBuilder = new ProcessBuilder(cmdArray);
final ProcessBuilder processBuilder = new ProcessBuilder(tokenizeCommandLine(JSType.toString(string)));
// Current ENV property state.
final Object env = global.get(ENV_NAME);
@ -239,4 +235,43 @@ public final class ScriptingFunctions {
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findStatic(MethodHandles.lookup(), ScriptingFunctions.class, name, MH.type(rtype, types));
}
/**
* Break an exec string into tokens, honoring quoted arguments and escaped
* spaces.
*
* @param execString a {@link String} with the command line to execute.
* @return a {@link List} of {@link String}s representing the tokens that
* constitute the command line.
* @throws IOException in case {@link StreamTokenizer#nextToken()} raises it.
*/
private static List<String> tokenizeCommandLine(final String execString) throws IOException {
final StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(execString));
tokenizer.resetSyntax();
tokenizer.wordChars(0, 255);
tokenizer.whitespaceChars(0, ' ');
tokenizer.commentChar('#');
tokenizer.quoteChar('"');
tokenizer.quoteChar('\'');
final List<String> cmdList = new ArrayList<>();
final StringBuilder toAppend = new StringBuilder();
while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
final String s = tokenizer.sval;
// The tokenizer understands about honoring quoted strings and recognizes
// them as one token that possibly contains multiple space-separated words.
// It does not recognize quoted spaces, though, and will split after the
// escaping \ character. This is handled here.
if (s.endsWith("\\")) {
// omit trailing \, append space instead
toAppend.append(s.substring(0, s.length() - 1)).append(' ');
} else {
cmdList.add(toAppend.append(s).toString());
toAppend.setLength(0);
}
}
if (toAppend.length() != 0) {
cmdList.add(toAppend.toString());
}
return cmdList;
}
}