@@ -0,0 +1,146 @@
|
||||
package com.github.shadowsocks.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Commandline objects help handling command lines specifying processes to
|
||||
* execute.
|
||||
*
|
||||
* The class can be used to define a command line as nested elements or as a
|
||||
* helper to define a command line by an application.
|
||||
* <p>
|
||||
* <code>
|
||||
* <someelement><br>
|
||||
* <acommandline executable="/executable/to/run"><br>
|
||||
* <argument value="argument 1" /><br>
|
||||
* <argument line="argument_1 argument_2 argument_3" /><br>
|
||||
* <argument value="argument 4" /><br>
|
||||
* </acommandline><br>
|
||||
* </someelement><br>
|
||||
* </code>
|
||||
*
|
||||
* Based on: https://github.com/apache/ant/blob/588ce1f/src/main/org/apache/tools/ant/types/Commandline.java
|
||||
*
|
||||
* Adds support for escape character '\'.
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
public final class Commandline {
|
||||
private Commandline() { }
|
||||
|
||||
/**
|
||||
* Quote the parts of the given array in way that makes them
|
||||
* usable as command line arguments.
|
||||
* @param line the list of arguments to quote.
|
||||
* @return empty string for null or no command, else every argument split
|
||||
* by spaces and quoted by quoting rules.
|
||||
*/
|
||||
public static String toString(String[] line) {
|
||||
// empty path return empty string
|
||||
if (line == null || line.length == 0) {
|
||||
return "";
|
||||
}
|
||||
// path containing one or more elements
|
||||
final StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < line.length; i++) {
|
||||
if (i > 0) {
|
||||
result.append(' ');
|
||||
}
|
||||
for (int j = 0; j < line[i].length(); ++j) {
|
||||
char ch = line[i].charAt(j);
|
||||
switch (ch) {
|
||||
case ' ': case '\\': case '"': case '\'': result.append('\\'); // intentionally no break
|
||||
default: result.append(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Crack a command line.
|
||||
* @param toProcess the command line to process.
|
||||
* @return the command line broken into strings.
|
||||
* An empty or null toProcess parameter results in a zero sized array.
|
||||
*/
|
||||
public static String[] translateCommandline(String toProcess) {
|
||||
if (toProcess == null || toProcess.length() == 0) {
|
||||
//no command? no string
|
||||
return new String[0];
|
||||
}
|
||||
// parse with a simple finite state machine
|
||||
|
||||
final int normal = 0;
|
||||
final int inQuote = 1;
|
||||
final int inDoubleQuote = 2;
|
||||
int state = normal;
|
||||
final StringTokenizer tok = new StringTokenizer(toProcess, "\\\"\' ", true);
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
final StringBuilder current = new StringBuilder();
|
||||
boolean lastTokenHasBeenQuoted = false;
|
||||
boolean lastTokenIsSlash = false;
|
||||
|
||||
while (tok.hasMoreTokens()) {
|
||||
String nextTok = tok.nextToken();
|
||||
switch (state) {
|
||||
case inQuote:
|
||||
if ("\'".equals(nextTok)) {
|
||||
lastTokenHasBeenQuoted = true;
|
||||
state = normal;
|
||||
} else {
|
||||
current.append(nextTok);
|
||||
}
|
||||
break;
|
||||
case inDoubleQuote:
|
||||
if ("\"".equals(nextTok)) {
|
||||
if (lastTokenIsSlash) {
|
||||
current.append(nextTok);
|
||||
lastTokenIsSlash = false;
|
||||
} else {
|
||||
lastTokenHasBeenQuoted = true;
|
||||
state = normal;
|
||||
}
|
||||
} else if ("\\".equals(nextTok)) {
|
||||
if (lastTokenIsSlash) {
|
||||
current.append(nextTok);
|
||||
lastTokenIsSlash = false;
|
||||
} else lastTokenIsSlash = true;
|
||||
} else {
|
||||
if (lastTokenIsSlash) {
|
||||
current.append("\\"); // unescaped
|
||||
lastTokenIsSlash = false;
|
||||
}
|
||||
current.append(nextTok);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (lastTokenIsSlash) {
|
||||
current.append(nextTok);
|
||||
lastTokenIsSlash = false;
|
||||
} else if ("\\".equals(nextTok)) lastTokenIsSlash = true; else if ("\'".equals(nextTok)) {
|
||||
state = inQuote;
|
||||
} else if ("\"".equals(nextTok)) {
|
||||
state = inDoubleQuote;
|
||||
} else if (" ".equals(nextTok)) {
|
||||
if (lastTokenHasBeenQuoted || current.length() != 0) {
|
||||
result.add(current.toString());
|
||||
current.setLength(0);
|
||||
}
|
||||
} else {
|
||||
current.append(nextTok);
|
||||
}
|
||||
lastTokenHasBeenQuoted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lastTokenHasBeenQuoted || current.length() != 0) {
|
||||
result.add(current.toString());
|
||||
}
|
||||
if (state == inQuote || state == inDoubleQuote) {
|
||||
throw new IllegalArgumentException("unbalanced quotes in " + toProcess);
|
||||
}
|
||||
if (lastTokenIsSlash) throw new IllegalArgumentException("escape character following nothing in " + toProcess);
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user