mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-14 22:37:06 -04:00
* Fix #2855: tokenize 应支持变量插值 * update * update * update * update * update
This commit is contained in:
parent
559aefd6b6
commit
030fba4630
@ -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
|
// To guarantee that when failed to generate launch command line, we will not call pre-launch command
|
||||||
List<String> rawCommandLine = command.commandLine.asList();
|
List<String> rawCommandLine = command.commandLine.asList();
|
||||||
if (StringUtils.isNotBlank(options.getWrapper())) {
|
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) {
|
if (command.tempNativeFolder != null) {
|
||||||
@ -452,7 +452,7 @@ public class DefaultLauncher extends Launcher {
|
|||||||
File runDirectory = repository.getRunDirectory(version.getId());
|
File runDirectory = repository.getRunDirectory(version.getId());
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(options.getPreLaunchCommand())) {
|
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());
|
builder.environment().putAll(getEnvVars());
|
||||||
SystemUtils.callExternalProcess(builder);
|
SystemUtils.callExternalProcess(builder);
|
||||||
}
|
}
|
||||||
@ -683,7 +683,7 @@ public class DefaultLauncher extends Launcher {
|
|||||||
|
|
||||||
if (StringUtils.isNotBlank(options.getPostExitCommand())) {
|
if (StringUtils.isNotBlank(options.getPostExitCommand())) {
|
||||||
try {
|
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());
|
builder.environment().putAll(getEnvVars());
|
||||||
SystemUtils.callExternalProcess(builder);
|
SystemUtils.callExternalProcess(builder);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
@ -207,93 +207,135 @@ public final class StringUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> tokenize(String str) {
|
private static boolean isVarNameStart(char ch) {
|
||||||
if (isBlank(str)) {
|
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> parseCommand(String command, Map<String, String> env) {
|
private static boolean isVarNamePart(char ch) {
|
||||||
StringBuilder stringBuilder = new StringBuilder(command);
|
return isVarNameStart(ch) || (ch >= '0' && ch <= '9');
|
||||||
for (Map.Entry<String, String> entry : env.entrySet()) {
|
}
|
||||||
String key = "$" + entry.getKey();
|
|
||||||
int i = 0;
|
private static int findVarEnd(String str, int offset) {
|
||||||
while (true) {
|
if (offset < str.length() - 1 && isVarNameStart(str.charAt(offset))) {
|
||||||
i = stringBuilder.indexOf(key, i);
|
int end = offset + 1;
|
||||||
if (i == -1) {
|
while (end < str.length()) {
|
||||||
|
if (!isVarNamePart(str.charAt(end))) {
|
||||||
break;
|
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) {
|
public static String parseColorEscapes(String original) {
|
||||||
|
@ -1,40 +1,61 @@
|
|||||||
package org.jackhuang.hmcl.util;
|
package org.jackhuang.hmcl.util;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class TokenizerTest {
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
private void test(String source, String... expected) {
|
|
||||||
Assertions.assertEquals(Arrays.asList(expected), StringUtils.tokenize(source));
|
public final class TokenizerTest {
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void textTokenizer() {
|
public void textTokenizer() {
|
||||||
test(
|
assertEquals(
|
||||||
"\"C:/Program Files/Bellsoft/JDK-11/bin.java.exe\" -version \"a.b.c\" something else",
|
Arrays.asList("C:/Program Files/Bellsoft/JDK-11/bin.java.exe", "-version", "a.b.c", "something", "else"),
|
||||||
"C:/Program Files/Bellsoft/JDK-11/bin.java.exe", "-version", "a.b.c", "something", "else"
|
StringUtils.tokenize("\"C:/Program Files/Bellsoft/JDK-11/bin.java.exe\" -version \"a.b.c\" something else")
|
||||||
);
|
);
|
||||||
test(
|
assertEquals(
|
||||||
"\"Another\"Text something else",
|
Arrays.asList("AnotherText", "something", "else"),
|
||||||
"AnotherText", "something", "else"
|
StringUtils.tokenize("\"Another\"Text something else")
|
||||||
);
|
);
|
||||||
test(
|
assertEquals(
|
||||||
"Text without quote",
|
Arrays.asList("Text", "without", "quote"),
|
||||||
"Text", "without", "quote"
|
StringUtils.tokenize("Text without quote")
|
||||||
);
|
);
|
||||||
test(
|
assertEquals(
|
||||||
"Text with multiple spaces",
|
Arrays.asList("Text", "with", "multiple", "spaces"),
|
||||||
"Text", "with", "multiple", "spaces"
|
StringUtils.tokenize("Text with multiple spaces")
|
||||||
);
|
);
|
||||||
test(
|
assertEquals(
|
||||||
"Text with empty part ''",
|
Arrays.asList("Text", "with", "empty", "part", ""),
|
||||||
"Text", "with", "empty", "part", ""
|
StringUtils.tokenize("Text with empty part ''")
|
||||||
);
|
);
|
||||||
test(
|
assertEquals(
|
||||||
"head\"abc\\n\\\\\\\"\"end",
|
Arrays.asList("headabc\n`\"$end"),
|
||||||
"headabc\n\\\"end"
|
StringUtils.tokenize("head\"abc`n```\"\"$end")
|
||||||
|
);
|
||||||
|
|
||||||
|
String instName = "1.20.4";
|
||||||
|
String instDir = "C:\\Program Files (x86)\\Minecraft\\";
|
||||||
|
|
||||||
|
Map<String, String> env = new HashMap<>();
|
||||||
|
env.put("INST_NAME", instName);
|
||||||
|
env.put("INST_DIR", instDir);
|
||||||
|
env.put("EMPTY", "");
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
Arrays.asList("cd", instDir),
|
||||||
|
StringUtils.tokenize("cd $INST_DIR", env)
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
Arrays.asList("Text", "with", "empty", "part", ""),
|
||||||
|
StringUtils.tokenize("Text with empty part $EMPTY", env)
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
Arrays.asList("head", "1.20.4", "$UNKNOWN", instDir, "", instDir + instName + "$UNKNOWN" + instDir + "$INST_DIR\n$UNKNOWN $$"),
|
||||||
|
StringUtils.tokenize("head $INST_NAME $UNKNOWN $INST_DIR $EMPTY $INST_DIR$INST_NAME$UNKNOWN\"$INST_DIR`$INST_DIR`n$UNKNOWN $EMPTY$\"$", env)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user