Fix #2855: tokenize 应支持变量插值 (#2858)

* Fix #2855: tokenize 应支持变量插值

* update

* update

* update

* update

* update
This commit is contained in:
Glavo
2024-02-27 21:12:51 +08:00
committed by GitHub
parent 559aefd6b6
commit 030fba4630
3 changed files with 169 additions and 106 deletions

View File

@@ -430,7 +430,7 @@ public class DefaultLauncher extends Launcher {
// To guarantee that when failed to generate launch command line, we will not call pre-launch command
List<String> rawCommandLine = command.commandLine.asList();
if (StringUtils.isNotBlank(options.getWrapper())) {
rawCommandLine.addAll(0, StringUtils.parseCommand(options.getWrapper(), getEnvVars()));
rawCommandLine.addAll(0, StringUtils.tokenize(options.getWrapper(), getEnvVars()));
}
if (command.tempNativeFolder != null) {
@@ -452,7 +452,7 @@ public class DefaultLauncher extends Launcher {
File runDirectory = repository.getRunDirectory(version.getId());
if (StringUtils.isNotBlank(options.getPreLaunchCommand())) {
ProcessBuilder builder = new ProcessBuilder(StringUtils.parseCommand(options.getPreLaunchCommand(), getEnvVars())).directory(runDirectory);
ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPreLaunchCommand(), getEnvVars())).directory(runDirectory);
builder.environment().putAll(getEnvVars());
SystemUtils.callExternalProcess(builder);
}
@@ -683,7 +683,7 @@ public class DefaultLauncher extends Launcher {
if (StringUtils.isNotBlank(options.getPostExitCommand())) {
try {
ProcessBuilder builder = new ProcessBuilder(StringUtils.parseCommand(options.getPostExitCommand(), getEnvVars())).directory(options.getGameDir());
ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPostExitCommand(), getEnvVars())).directory(options.getGameDir());
builder.environment().putAll(getEnvVars());
SystemUtils.callExternalProcess(builder);
} catch (Throwable e) {

View File

@@ -207,93 +207,135 @@ public final class StringUtils {
return false;
}
public static List<String> tokenize(String str) {
if (isBlank(str)) {
return new ArrayList<>();
} else {
// Split the string with ' and space cleverly.
ArrayList<String> parts = new ArrayList<>();
boolean hasValue = false;
StringBuilder current = new StringBuilder(str.length());
for (int i = 0; i < str.length(); ) {
char c = str.charAt(i);
if (c == '\'') {
hasValue = true;
int end = str.indexOf(c, i + 1);
if (end < 0) {
end = str.length();
}
current.append(str, i + 1, end);
i = end + 1;
} else if (c == '"') {
hasValue = true;
i++;
while (i < str.length()) {
c = str.charAt(i++);
if (c == '"') {
break;
} else if (c == '\\' && i < str.length()) {
c = str.charAt(i++);
switch (c) {
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'v':
c = '\u000b';
break;
case 'a':
c = '\u0007';
break;
}
current.append(c);
} else {
current.append(c);
}
}
} else if (c == ' ') {
if (hasValue) {
parts.add(current.toString());
current.setLength(0);
hasValue = false;
}
i++;
} else {
hasValue = true;
current.append(c);
i++;
}
}
if (hasValue) {
parts.add(current.toString());
}
return parts;
}
private static boolean isVarNameStart(char ch) {
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
}
public static List<String> parseCommand(String command, Map<String, String> env) {
StringBuilder stringBuilder = new StringBuilder(command);
for (Map.Entry<String, String> entry : env.entrySet()) {
String key = "$" + entry.getKey();
int i = 0;
while (true) {
i = stringBuilder.indexOf(key, i);
if (i == -1) {
private static boolean isVarNamePart(char ch) {
return isVarNameStart(ch) || (ch >= '0' && ch <= '9');
}
private static int findVarEnd(String str, int offset) {
if (offset < str.length() - 1 && isVarNameStart(str.charAt(offset))) {
int end = offset + 1;
while (end < str.length()) {
if (!isVarNamePart(str.charAt(end))) {
break;
}
stringBuilder.replace(i, i + key.length(), entry.getValue());
end++;
}
return end;
}
return tokenize(stringBuilder.toString());
return -1;
}
public static List<String> tokenize(String str) {
return tokenize(str, null);
}
public static List<String> tokenize(String str, Map<String, String> vars) {
if (isBlank(str)) {
return new ArrayList<>();
}
if (vars == null) {
vars = Collections.emptyMap();
}
// Split the string with ' and space cleverly.
ArrayList<String> parts = new ArrayList<>();
int varEnd;
boolean hasValue = false;
StringBuilder current = new StringBuilder(str.length());
for (int i = 0; i < str.length(); ) {
char c = str.charAt(i);
if (c == '\'') {
hasValue = true;
int end = str.indexOf(c, i + 1);
if (end < 0) {
end = str.length();
}
current.append(str, i + 1, end);
i = end + 1;
} else if (c == '"') {
hasValue = true;
i++;
while (i < str.length()) {
c = str.charAt(i++);
if (c == '"') {
break;
} else if (c == '`' && i < str.length()) {
c = str.charAt(i++);
switch (c) {
case 'a':
c = '\u0007';
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'v':
c = '\u000b';
break;
}
current.append(c);
} else if (c == '$' && (varEnd = findVarEnd(str, i)) >= 0) {
String key = str.substring(i, varEnd);
String value = vars.get(key);
if (value != null) {
current.append(value);
} else {
current.append('$').append(key);
}
i = varEnd;
} else {
current.append(c);
}
}
} else if (c == ' ') {
if (hasValue) {
parts.add(current.toString());
current.setLength(0);
hasValue = false;
}
i++;
} else if (c == '$' && (varEnd = findVarEnd(str, i + 1)) >= 0) {
hasValue = true;
String key = str.substring(i + 1, varEnd);
String value = vars.get(key);
if (value != null) {
current.append(value);
} else {
current.append('$').append(key);
}
i = varEnd;
} else {
hasValue = true;
current.append(c);
i++;
}
}
if (hasValue) {
parts.add(current.toString());
}
return parts;
}
public static String parseColorEscapes(String original) {