diff options
author | Markus Keller | 2012-09-05 15:59:51 +0000 |
---|---|---|
committer | Mike Rennie | 2012-09-05 15:59:51 +0000 |
commit | 37978528c6f546dbacda8aaa7c9b284e602b5b44 (patch) | |
tree | cee56b51c5b3edecff7ec849a07e50974cc6243b /org.eclipse.debug.core | |
parent | 176d9f36b174c79199db39afe242047fc5d6290d (diff) | |
download | eclipse.platform.debug-37978528c6f546dbacda8aaa7c9b284e602b5b44.tar.gz eclipse.platform.debug-37978528c6f546dbacda8aaa7c9b284e602b5b44.tar.xz eclipse.platform.debug-37978528c6f546dbacda8aaa7c9b284e602b5b44.zip |
Bug 387504 - Bugs in program argument parsing (compared to command line)v20120905-155951
Diffstat (limited to 'org.eclipse.debug.core')
-rw-r--r-- | org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java | 355 |
1 files changed, 256 insertions, 99 deletions
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java b/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java index 463920b9f..13b6029b8 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java @@ -853,6 +853,13 @@ public class DebugPlugin extends Plugin { * @since 3.0 */ public static Process exec(String[] cmdLine, File workingDirectory, String[] envp) throws CoreException { + if (Platform.getOS().equals(Constants.OS_WIN32)) { + String[] winCmdLine= new String[cmdLine.length]; + for (int i= 0; i < cmdLine.length; i++) { + winCmdLine[i]= winQuote(cmdLine[i]); + } + cmdLine= winCmdLine; + } Process p= null; try { if (workingDirectory == null) { @@ -876,7 +883,28 @@ public class DebugPlugin extends Plugin { } } return p; - } + } + + private static boolean needsQuoting(String s) { + int len = s.length(); + if (len == 0) // empty string has to be quoted + return true; + for (int i = 0; i < len; i++) { + switch (s.charAt(i)) { + case ' ': case '\t': case '\\': case '"': + return true; + } + } + return false; + } + + private static String winQuote(String s) { + if (! needsQuoting(s)) + return s; + s = s.replaceAll("([\\\\]*)\"", "$1$1\\\\\""); //$NON-NLS-1$ //$NON-NLS-2$ + s = s.replaceAll("([\\\\]*)\\z", "$1$1"); //$NON-NLS-1$ //$NON-NLS-2$ + return "\"" + s + "\""; //$NON-NLS-1$ //$NON-NLS-2$ + } /** * Returns whether this plug-in is in the process of @@ -1256,115 +1284,168 @@ public class DebugPlugin extends Plugin { throw new CoreException(status); } - /** - * Utility class to parse command line arguments. - * - * @since 3.1 - */ - private static class ArgumentParser { - private String fArgs; - private int fIndex= 0; - private int ch= -1; - - public ArgumentParser(String args) { - fArgs= args; - } - - public String[] parseArguments() { - List v= new ArrayList(); - - ch= getNext(); - while (ch > 0) { - if (Character.isWhitespace((char)ch)) { - ch= getNext(); + private static String[] parseArgumentsWindows(String args) { + // see http://msdn.microsoft.com/en-us/library/a1y7w461.aspx + List result= new ArrayList(); + + final int DEFAULT= 0; + final int ARG= 1; + final int IN_DOUBLE_QUOTE= 2; + + int state= DEFAULT; + int backslashes= 0; + StringBuffer buf= new StringBuffer(); + int len= args.length(); + for (int i= 0; i < len; i++) { + char ch= args.charAt(i); + if (ch == '\\') { + backslashes++; + continue; + } else if (backslashes != 0) { + if (ch == '"') { + for (; backslashes >= 2; backslashes-= 2) { + buf.append('\\'); + } + if (backslashes == 1) { + if (state == DEFAULT) + state= ARG; + buf.append('"'); + backslashes= 0; + continue; + } // else fall through to switch } else { - if (ch == '"') { - StringBuffer buf = new StringBuffer(); - buf.append(parseString()); - if (buf.length() == 0 && Platform.getOS().equals(Constants.OS_WIN32)) { - // empty string on windows platform - buf.append("\"\""); //$NON-NLS-1$ - } - v.add(buf.toString()); - } else { - v.add(parseToken()); + // false alarm, treat passed backslashes literally... + if (state == DEFAULT) + state= ARG; + for (; backslashes > 0; backslashes--) { + buf.append('\\'); } + // fall through to switch } } - - String[] result= new String[v.size()]; - v.toArray(result); - return result; - } - - private int getNext() { - if (fIndex < fArgs.length()) - return fArgs.charAt(fIndex++); - return -1; - } - - private String parseString() { - ch= getNext(); - if (ch == '"') { - ch= getNext(); - return ""; //$NON-NLS-1$ + if (Character.isWhitespace(ch)) { + if (state == DEFAULT) { + // skip + continue; + } else if (state == ARG) { + state= DEFAULT; + result.add(buf.toString()); + buf.setLength(0); + continue; + } } - StringBuffer buf= new StringBuffer(); - while (ch > 0 && ch != '"') { - if (ch == '\\') { - ch= getNext(); - if (ch != '"') { // Only escape double quotes - buf.append('\\'); + switch (state) { + case DEFAULT: + case ARG: + if (ch == '"') { + state= IN_DOUBLE_QUOTE; } else { - if (Platform.getOS().equals(Constants.OS_WIN32)) { - // @see Bug 26870. Windows requires an extra escape for embedded strings - buf.append('\\'); + state= ARG; + buf.append(ch); + } + break; + + case IN_DOUBLE_QUOTE: + if (ch == '"') { + if (i + 1 < len && args.charAt(i + 1) == '"') { + /* Undocumented feature in Windows: + * Two consecutive double quotes inside a double-quoted argument are interpreted as + * a single double quote. + */ + buf.append('"'); + i++; + } else if (buf.length() == 0) { + // empty string on Windows platform. Account for bug in constructor of JDK's java.lang.ProcessImpl. + result.add("\"\""); //$NON-NLS-1$ + state= DEFAULT; + } else { + state= ARG; } + } else { + buf.append(ch); } - } - if (ch > 0) { - buf.append((char)ch); - ch= getNext(); - } + break; + + default: + throw new IllegalStateException(); } - ch= getNext(); - return buf.toString(); } - - private String parseToken() { - StringBuffer buf= new StringBuffer(); - - while (ch > 0 && !Character.isWhitespace((char)ch)) { - if (ch == '\\') { - ch= getNext(); - if (Character.isWhitespace((char)ch)) { - // end of token, don't lose trailing backslash - buf.append('\\'); - return buf.toString(); + if (buf.length() > 0) + result.add(buf.toString()); + + return (String[]) result.toArray(new String[result.size()]); + } + + private static String[] parseArgumentsImpl(String args) { + // man sh, see topic QUOTING + List result= new ArrayList(); + + final int DEFAULT= 0; + final int ARG= 1; + final int IN_DOUBLE_QUOTE= 2; + final int IN_SINGLE_QUOTE= 3; + + int state= DEFAULT; + StringBuffer buf= new StringBuffer(); + int len= args.length(); + for (int i= 0; i < len; i++) { + char ch= args.charAt(i); + if (Character.isWhitespace(ch)) { + if (state == DEFAULT) { + // skip + continue; + } else if (state == ARG) { + state= DEFAULT; + result.add(buf.toString()); + buf.setLength(0); + continue; + } + } + switch (state) { + case DEFAULT: + case ARG: + if (ch == '"') { + state= IN_DOUBLE_QUOTE; + } else if (ch == '\'') { + state= IN_SINGLE_QUOTE; + } else if (ch == '\\' && i + 1 < len) { + state= ARG; + ch= args.charAt(++i); + buf.append(ch); + } else { + state= ARG; + buf.append(ch); } - if (ch > 0) { - if (ch != '"') { // Only escape double quotes - buf.append('\\'); - } else { - if (Platform.getOS().equals(Constants.OS_WIN32)) { - // @see Bug 26870. Windows requires an extra escape for embedded strings - buf.append('\\'); - } - } - buf.append((char)ch); - ch= getNext(); - } else if (ch == -1) { // Don't lose a trailing backslash - buf.append('\\'); + break; + + case IN_DOUBLE_QUOTE: + if (ch == '"') { + state= ARG; + } else if (ch == '\\' && i + 1 < len && + (args.charAt(i + 1) == '\\' || args.charAt(i + 1) == '"')) { + ch= args.charAt(++i); + buf.append(ch); + } else { + buf.append(ch); } - } else if (ch == '"') { - buf.append(parseString()); - } else { - buf.append((char)ch); - ch= getNext(); - } + break; + + case IN_SINGLE_QUOTE: + if (ch == '\'') { + state= ARG; + } else { + buf.append(ch); + } + break; + + default: + throw new IllegalStateException(); } - return buf.toString(); } + if (buf.length() > 0) + result.add(buf.toString()); + + return (String[]) result.toArray(new String[result.size()]); } /** @@ -1379,13 +1460,89 @@ public class DebugPlugin extends Plugin { public static String[] parseArguments(String args) { if (args == null) return new String[0]; - ArgumentParser parser= new ArgumentParser(args); - String[] res= parser.parseArguments(); - return res; + if (Constants.OS_WIN32.equals(Platform.getOS())) + return parseArgumentsWindows(args); + + return parseArgumentsImpl(args); } /** + * Renders the given array of strings into a single command line. + * <p> + * If <code>segments</code> is not <code>null</code>, the array is filled + * with the offsets of the start positions of arguments 1 to + * <code>arguments.length - 1</code>, as rendered in the resulting string. + * </p> + * + * @param arguments + * the command line arguments + * @param segments + * an array of size <code>arguments.length - 1</code> or + * <code>null</code> + * @return the command line + * @since 3.8 + */ + public static String renderArguments(String[] arguments, int[] segments) { + boolean isWin32= Platform.getOS().equals(Constants.OS_WIN32); + StringBuffer buf = new StringBuffer(); + int count = arguments.length; + for (int i = 0; i < count; i++) { + if (i > 0) + buf.append(' '); + + boolean containsSpace = false; + char[] characters = arguments[i].toCharArray(); + for (int j = 0; j < characters.length; j++) { + char ch = characters[j]; + if (ch == ' ' || ch == '\t') { + containsSpace = true; + buf.append('"'); + break; + } + } + + int backslashes = 0; + for (int j = 0; j < characters.length; j++) { + char ch = characters[j]; + if (ch == '"') { + if (isWin32) { + if (j == 0 && characters.length == 2 && characters[1] == '"') { + // empty string on windows platform, see bug 130767. Bug in constructor of JDK's java.lang.ProcessImpl. + buf.append("\"\""); //$NON-NLS-1$ + break; + } + if (backslashes > 0) { + // Feature in Windows: need to double-escape backslashes in front of double quote. + for (; backslashes > 0; backslashes--) { + buf.append('\\'); + } + } + } + buf.append('\\'); + } else if (ch == '\\') { + if (isWin32) { + backslashes++; + } else { + buf.append('\\'); + } + } + buf.append(ch); + } + if (containsSpace) { + buf.append('"'); + } else if (characters.length == 0) { + buf.append("\"\""); //$NON-NLS-1$ + } + + if (segments != null && i < count - 1) { + segments[i] = buf.length() + 1; + } + } + return buf.toString(); + } + + /** * Sets whether step filters should be applied to step commands. This * setting is a global option applied to all registered debug targets. * |