mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-105472 (correct quoting of Windows shell commands)
This commit is contained in:
+13
-41
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.execution.configurations;
|
||||
|
||||
import com.intellij.execution.CommandLineUtil;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.process.ProcessNotCreatedException;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
@@ -22,7 +23,6 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -55,7 +55,6 @@ public class GeneralCommandLine implements UserDataHolder {
|
||||
Key.create("GeneralCommandLine.do.not.escape.quotes");
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.execution.configurations.GeneralCommandLine");
|
||||
private static final char QUOTE = '\uEFEF';
|
||||
|
||||
private String myExePath = null;
|
||||
private File myWorkDirectory = null;
|
||||
@@ -70,12 +69,12 @@ public class GeneralCommandLine implements UserDataHolder {
|
||||
public GeneralCommandLine() {
|
||||
}
|
||||
|
||||
public GeneralCommandLine(final String... command) {
|
||||
public GeneralCommandLine(@NotNull String... command) {
|
||||
this(Arrays.asList(command));
|
||||
}
|
||||
|
||||
public GeneralCommandLine(final List<String> command) {
|
||||
final int size = command.size();
|
||||
public GeneralCommandLine(@NotNull List<String> command) {
|
||||
int size = command.size();
|
||||
if (size > 0) {
|
||||
setExePath(command.get(0));
|
||||
if (size > 1) {
|
||||
@@ -211,13 +210,14 @@ public class GeneralCommandLine implements UserDataHolder {
|
||||
|
||||
/**
|
||||
* Prepares command (quotes and escapes all arguments) and returns it as a newline-separated list
|
||||
* (suitable e.g. for passing in a environment variable).
|
||||
* (suitable e.g. for passing in an environment variable).
|
||||
*
|
||||
* @return command as a newline-separated list.
|
||||
*/
|
||||
@NotNull
|
||||
public String getPreparedCommandLine() {
|
||||
return StringUtil.join(prepareCommands(), "\n");
|
||||
String exePath = myExePath != null ? myExePath : "";
|
||||
return StringUtil.join(CommandLineUtil.toCommandLine(exePath, myProgramParams.getList()), "\n");
|
||||
}
|
||||
|
||||
public Process createProcess() throws ExecutionException {
|
||||
@@ -225,14 +225,15 @@ public class GeneralCommandLine implements UserDataHolder {
|
||||
LOG.debug("Executing [" + getCommandLineString() + "]");
|
||||
}
|
||||
|
||||
String[] commands;
|
||||
List<String> commands;
|
||||
try {
|
||||
checkWorkingDirectory();
|
||||
|
||||
commands = prepareCommands();
|
||||
if (StringUtil.isEmptyOrSpaces(commands[0])) {
|
||||
if (StringUtil.isEmptyOrSpaces(myExePath)) {
|
||||
throw new ExecutionException(IdeBundle.message("run.configuration.error.executable.not.specified"));
|
||||
}
|
||||
|
||||
commands = CommandLineUtil.toCommandLine(myExePath, myProgramParams.getList());
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
LOG.warn(e);
|
||||
@@ -241,8 +242,7 @@ public class GeneralCommandLine implements UserDataHolder {
|
||||
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder(commands);
|
||||
Map<String, String> environment = builder.environment();
|
||||
setupEnvironment(environment);
|
||||
setupEnvironment(builder.environment());
|
||||
builder.directory(myWorkDirectory);
|
||||
builder.redirectErrorStream(myRedirectErrorStream);
|
||||
return builder.start();
|
||||
@@ -266,34 +266,6 @@ public class GeneralCommandLine implements UserDataHolder {
|
||||
}
|
||||
}
|
||||
|
||||
private String[] prepareCommands() {
|
||||
final List<String> parameters = myProgramParams.getList();
|
||||
final String[] result = new String[parameters.size() + 1];
|
||||
result[0] = myExePath != null ? prepareCommand(FileUtil.toSystemDependentName(myExePath)) : null;
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
result[i + 1] = prepareCommand(parameters.get(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// please keep in sync with com.intellij.rt.execution.junit.ProcessBuilder.prepareCommand() && org.jetbrains.jps.incremental.ExternalProcessUtil.prepareCommand()
|
||||
public static String prepareCommand(String parameter) {
|
||||
if (SystemInfo.isWindows) {
|
||||
if (parameter.contains("\"")) {
|
||||
parameter = StringUtil.replace(parameter, "\"", "\\\"");
|
||||
}
|
||||
else if (parameter.length() == 0) {
|
||||
parameter = "\"\"";
|
||||
}
|
||||
}
|
||||
|
||||
if (parameter.length() >= 2 && parameter.charAt(0) == QUOTE && parameter.charAt(parameter.length() - 1) == QUOTE) {
|
||||
parameter = '"' + parameter.substring(1, parameter.length() - 1) + '"';
|
||||
}
|
||||
|
||||
return parameter;
|
||||
}
|
||||
|
||||
private void setupEnvironment(final Map<String, String> environment) {
|
||||
if (!myPassParentEnvironment) {
|
||||
environment.clear();
|
||||
@@ -327,7 +299,7 @@ public class GeneralCommandLine implements UserDataHolder {
|
||||
*/
|
||||
@NotNull
|
||||
public static String inescapableQuote(@NotNull String parameter) {
|
||||
return QUOTE + parameter + QUOTE;
|
||||
return CommandLineUtil.specialQuote(parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.execution;
|
||||
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CommandLineUtil {
|
||||
private static final char SPECIAL_QUOTE = '\uEFEF';
|
||||
private static final String WIN_SHELL_SPECIALS = "&<>()@^|";
|
||||
|
||||
public static @NotNull String specialQuote(@NotNull String parameter) {
|
||||
return quote(parameter, SPECIAL_QUOTE);
|
||||
}
|
||||
|
||||
public static @NotNull List<String> toCommandLine(@NotNull List<String> command) {
|
||||
assert command.size() > 0;
|
||||
return toCommandLine(command.get(0), command.subList(1, command.size()));
|
||||
}
|
||||
|
||||
// please keep an implementation in sync with [junit-rt] ProcessBuilder.createProcess()
|
||||
public static @NotNull List<String> toCommandLine(@NotNull String command, @NotNull List<String> parameters) {
|
||||
List<String> commandLine = ContainerUtil.newArrayListWithExpectedSize(parameters.size() + 1);
|
||||
|
||||
commandLine.add(FileUtil.toSystemDependentName(command));
|
||||
|
||||
boolean winShell = SystemInfo.isWindows &&
|
||||
("cmd".equalsIgnoreCase(command) || "cmd.exe".equalsIgnoreCase(command)) &&
|
||||
parameters.size() > 1 && "/c".equalsIgnoreCase(parameters.get(0));
|
||||
|
||||
for (String parameter : parameters) {
|
||||
if (SystemInfo.isWindows) {
|
||||
if (parameter.contains("\"")) {
|
||||
parameter = StringUtil.replace(parameter, "\"", "\\\"");
|
||||
}
|
||||
else if (parameter.length() == 0) {
|
||||
parameter = "\"\"";
|
||||
}
|
||||
}
|
||||
|
||||
if (winShell && StringUtil.containsAnyChar(parameter, WIN_SHELL_SPECIALS)) {
|
||||
parameter = quote(parameter, SPECIAL_QUOTE);
|
||||
}
|
||||
|
||||
if (isQuoted(parameter, SPECIAL_QUOTE)) {
|
||||
parameter = quote(parameter.substring(1, parameter.length() - 1), '"');
|
||||
}
|
||||
|
||||
commandLine.add(parameter);
|
||||
}
|
||||
|
||||
return commandLine;
|
||||
}
|
||||
|
||||
private static String quote(String s, char ch) {
|
||||
return !isQuoted(s, ch) ? ch + s + ch : s;
|
||||
}
|
||||
|
||||
private static boolean isQuoted(String s, char ch) {
|
||||
return s.length() >= 2 && s.charAt(0) == ch && s.charAt(s.length() - 1) == ch;
|
||||
}
|
||||
}
|
||||
+11
-8
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.jetbrains.plugins.javaFX.packaging;
|
||||
|
||||
import com.intellij.execution.CommandLineUtil;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
@@ -23,7 +24,10 @@ import com.intellij.util.Base64Converter;
|
||||
import com.intellij.util.PathUtilRt;
|
||||
import com.intellij.util.io.ZipUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -97,7 +101,7 @@ public abstract class AbstractJavaFxPackager {
|
||||
buf.append("<target name=\"build artifact\" xmlns:fx=\"javafx:com.sun.javafx.tools.ant\">");
|
||||
final String artifactFileName = getArtifactRootName();
|
||||
final String artifactName = FileUtil.getNameWithoutExtension(artifactFileName);
|
||||
final List<JavaFxAntGenerator.SimpleTag> tags =
|
||||
final List<JavaFxAntGenerator.SimpleTag> tags =
|
||||
JavaFxAntGenerator.createJarAndDeployTasks(this, artifactFileName, artifactName, tempUnzippedArtifactOutput.getPath());
|
||||
for (JavaFxAntGenerator.SimpleTag tag : tags) {
|
||||
tag.generate(buf);
|
||||
@@ -228,16 +232,15 @@ public abstract class AbstractJavaFxPackager {
|
||||
registerJavaFxPackagerError(ex.getMessage());
|
||||
}
|
||||
|
||||
protected abstract String prepareParam(String param);
|
||||
private void addParameter(List<String> commandLine, String param) {
|
||||
private static void addParameter(List<String> commandLine, String param) {
|
||||
if (!StringUtil.isEmptyOrSpaces(param)) {
|
||||
commandLine.add(prepareParam(param));
|
||||
commandLine.add(param);
|
||||
}
|
||||
}
|
||||
|
||||
private int startProcess(List<String> commands) {
|
||||
try {
|
||||
final Process process = new ProcessBuilder(commands).start();
|
||||
final Process process = new ProcessBuilder(CommandLineUtil.toCommandLine(commands)).start();
|
||||
final String message = new String(FileUtil.loadBytes(process.getErrorStream()));
|
||||
if (!StringUtil.isEmptyOrSpaces(message)) {
|
||||
registerJavaFxPackagerError(message);
|
||||
@@ -262,9 +265,9 @@ public abstract class AbstractJavaFxPackager {
|
||||
commands.add("-Dant.home=" + antHome);
|
||||
|
||||
commands.add("-classpath");
|
||||
commands.add(antHome + "/lib/ant.jar" + File.pathSeparator +
|
||||
commands.add(antHome + "/lib/ant.jar" + File.pathSeparator +
|
||||
antHome + "/lib/ant-launcher.jar" + File.pathSeparator +
|
||||
javaHome + "/lib/ant-javafx.jar" + File.pathSeparator +
|
||||
javaHome + "/lib/ant-javafx.jar" + File.pathSeparator +
|
||||
javaHome + "/jre/lib/jfxrt.jar");
|
||||
commands.add("org.apache.tools.ant.launch.Launcher");
|
||||
commands.add("-f");
|
||||
|
||||
+2
-7
@@ -154,7 +154,7 @@ public class JavaFxAntTaskTest extends UsefulTestCase{
|
||||
if (preloaderJar != null) {
|
||||
packager.setPreloaderJar(preloaderJar);
|
||||
}
|
||||
|
||||
|
||||
final List<JavaFxAntGenerator.SimpleTag> temp = JavaFxAntGenerator
|
||||
.createJarAndDeployTasks(packager, artifactFileName, artifactName, "temp");
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
@@ -163,7 +163,7 @@ public class JavaFxAntTaskTest extends UsefulTestCase{
|
||||
}
|
||||
assertEquals(expected
|
||||
.replaceAll("temp/deploy", "temp\\" + File.separator + "deploy")
|
||||
.replaceAll("temp/" + artifactFileName, "temp\\" + File.separator + artifactFileName),
|
||||
.replaceAll("temp/" + artifactFileName, "temp\\" + File.separator + artifactFileName),
|
||||
buf.toString());
|
||||
}
|
||||
|
||||
@@ -275,11 +275,6 @@ public class JavaFxAntTaskTest extends UsefulTestCase{
|
||||
protected void registerJavaFxPackagerError(String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prepareParam(String param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeypass() {
|
||||
return null;
|
||||
|
||||
+15
-6
@@ -1,10 +1,24 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.javaFX;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.builders.artifacts.ArtifactBuildTaskProvider;
|
||||
import org.jetbrains.jps.incremental.BuildTask;
|
||||
import org.jetbrains.jps.incremental.CompileContext;
|
||||
import org.jetbrains.jps.incremental.ExternalProcessUtil;
|
||||
import org.jetbrains.jps.incremental.ProjectBuildException;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.incremental.messages.CompilerMessage;
|
||||
@@ -144,11 +158,6 @@ public class JpsJavaFxArtifactBuildTaskProvider extends ArtifactBuildTaskProvide
|
||||
myCompileContext.processMessage(new CompilerMessage(COMPILER_NAME, BuildMessage.Kind.ERROR, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prepareParam(String param) {
|
||||
return ExternalProcessUtil.prepareCommand(param);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHtmlParamFile() {
|
||||
return myProperties.myState.getHtmlParamFile();
|
||||
|
||||
+1
-7
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.jetbrains.plugins.javaFX.packaging;
|
||||
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.compiler.CompileContext;
|
||||
import com.intellij.openapi.compiler.CompilerMessageCategory;
|
||||
@@ -370,16 +369,11 @@ public class JavaFxArtifactProperties extends ArtifactProperties<JavaFxArtifactP
|
||||
return myProperties.isConvertCss2Bin();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prepareParam(String param) {
|
||||
return GeneralCommandLine.prepareCommand(param);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHtmlParamFile() {
|
||||
return myProperties.getHtmlParamFile();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getParamFile() {
|
||||
return myProperties.getParamFile();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,6 +25,8 @@ import java.util.List;
|
||||
public class ProcessBuilder {
|
||||
public static final boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
|
||||
|
||||
private static final String WIN_SHELL_SPECIALS = "&<>()@^|";
|
||||
|
||||
private final List myParameters = new ArrayList();
|
||||
|
||||
public void add(final String parameter) {
|
||||
@@ -37,36 +39,55 @@ public class ProcessBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// please keep an implementation in sync with [util] CommandLineUtil.toCommandLine()
|
||||
public Process createProcess() throws IOException {
|
||||
if (myParameters.size() < 1) {
|
||||
throw new IllegalArgumentException("Executable name not specified");
|
||||
}
|
||||
|
||||
final String[] command = new String[myParameters.size()];
|
||||
for (int i = 0; i < myParameters.size(); i++) {
|
||||
command[i] = prepareCommand(myParameters.get(i).toString());
|
||||
String command = myParameters.get(0).toString();
|
||||
boolean winShell = isWindows &&
|
||||
("cmd".equalsIgnoreCase(command) || "cmd.exe".equalsIgnoreCase(command)) &&
|
||||
myParameters.size() > 1 && "/c".equalsIgnoreCase(myParameters.get(0).toString());
|
||||
|
||||
String[] commandLine = new String[myParameters.size()];
|
||||
commandLine[0] = command;
|
||||
|
||||
for (int i = 1; i < myParameters.size(); i++) {
|
||||
String parameter = myParameters.get(i).toString();
|
||||
|
||||
if (isWindows) {
|
||||
int pos = parameter.indexOf('\"');
|
||||
if (pos >= 0) {
|
||||
StringBuffer buffer = new StringBuffer(parameter);
|
||||
do {
|
||||
buffer.insert(pos, '\\');
|
||||
pos += 2;
|
||||
}
|
||||
while ((pos = parameter.indexOf('\"', pos)) >= 0);
|
||||
parameter = buffer.toString();
|
||||
}
|
||||
else if (parameter.length() == 0) {
|
||||
parameter = "\"\"";
|
||||
}
|
||||
|
||||
if (winShell && containsAnyChar(parameter, WIN_SHELL_SPECIALS)) {
|
||||
parameter = '"' + parameter + '"';
|
||||
}
|
||||
}
|
||||
|
||||
commandLine[i] = parameter;
|
||||
}
|
||||
|
||||
return Runtime.getRuntime().exec(command);
|
||||
return Runtime.getRuntime().exec(commandLine);
|
||||
}
|
||||
|
||||
// please keep in sync with GeneralCommandLine.prepareCommand()
|
||||
private static String prepareCommand(String parameter) {
|
||||
if (isWindows) {
|
||||
int pos = parameter.indexOf('\"');
|
||||
if (pos >= 0) {
|
||||
final StringBuffer buffer = new StringBuffer(parameter);
|
||||
do {
|
||||
buffer.insert(pos, '\\');
|
||||
pos += 2;
|
||||
}
|
||||
while ((pos = parameter.indexOf('\"', pos)) >= 0);
|
||||
parameter = buffer.toString();
|
||||
}
|
||||
else if (parameter.length() == 0) {
|
||||
parameter = "\"\"";
|
||||
private static boolean containsAnyChar(String value, String chars) {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
if (chars.indexOf(value.charAt(i)) >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return parameter;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user