8236919: Refactor com.sun.tools.javac.main.CommandLine into a reusable module for other JDK tools
Reviewed-by: jjg
This commit is contained in:
parent
a249a52501
commit
a80552e1e1
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,34 +23,38 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.jpackage.main;
|
||||
package jdk.internal.opt;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
/*
|
||||
* This file was originally a copy of CommandLine.java in
|
||||
* com.sun.tools.javac.main.
|
||||
* It should track changes made to that file.
|
||||
* com.sun.tools.javac.main -- and it will be the last.
|
||||
*
|
||||
* Find details at https://bugs.openjdk.org/browse/JDK-8236919
|
||||
*/
|
||||
|
||||
/**
|
||||
* Various utility methods for processing Java tool command line arguments.
|
||||
*
|
||||
* <p><b>This is NOT part of any supported API.
|
||||
* If you write code that depends on this, you do so at your own risk.
|
||||
* This code and its internal interfaces are subject to change or
|
||||
* deletion without notice.</b>
|
||||
*/
|
||||
class CommandLine {
|
||||
public final class CommandLine {
|
||||
/**
|
||||
* Convenient wrapper for the {@code List}-based parse method.
|
||||
*
|
||||
* @see #parse(List)
|
||||
*/
|
||||
public static String[] parse(String... args) throws IOException {
|
||||
return parse(List.of(args)).toArray(String[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Win32-style command files for the specified command line
|
||||
* arguments and return the resulting arguments. A command file argument
|
||||
@ -64,14 +68,13 @@ class CommandLine {
|
||||
* @return the arguments, with @files expanded
|
||||
* @throws IOException if there is a problem reading any of the @files
|
||||
*/
|
||||
public static String[] parse(String[] args) throws IOException {
|
||||
public static List<String> parse(List<String> args) throws IOException {
|
||||
List<String> newArgs = new ArrayList<>();
|
||||
appendParsedCommandArgs(newArgs, Arrays.asList(args));
|
||||
return newArgs.toArray(new String[newArgs.size()]);
|
||||
appendParsedCommandArgs(newArgs, args);
|
||||
return newArgs;
|
||||
}
|
||||
|
||||
private static void appendParsedCommandArgs(List<String> newArgs,
|
||||
List<String> args) throws IOException {
|
||||
private static void appendParsedCommandArgs(List<String> newArgs, List<String> args) throws IOException {
|
||||
for (String arg : args) {
|
||||
if (arg.length() > 1 && arg.charAt(0) == '@') {
|
||||
arg = arg.substring(1);
|
||||
@ -86,13 +89,45 @@ class CommandLine {
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadCmdFile(String name, List<String> args)
|
||||
throws IOException {
|
||||
if (!Files.isReadable(Path.of(name))) {
|
||||
throw new FileNotFoundException(name);
|
||||
}
|
||||
try (Reader r = Files.newBufferedReader(Paths.get(name),
|
||||
Charset.defaultCharset())) {
|
||||
/**
|
||||
* Process the given environment variable and appends any Win32-style
|
||||
* command files for the specified command line arguments and return
|
||||
* the resulting arguments. A command file argument
|
||||
* is of the form '@file' where 'file' is the name of the file whose
|
||||
* contents are to be parsed for additional arguments. The contents of
|
||||
* the command file are parsed using StreamTokenizer and the original
|
||||
* '@file' argument replaced with the resulting tokens. Recursive command
|
||||
* files are not supported. The '@' character itself can be quoted with
|
||||
* the sequence '@@'.
|
||||
* @param envVariable the env variable to process
|
||||
* @param args the arguments that may contain @files
|
||||
* @return the arguments, with environment variable's content and expansion of @files
|
||||
* @throws IOException if there is a problem reading any of the @files
|
||||
* @throws CommandLine.UnmatchedQuote
|
||||
*/
|
||||
public static List<String> parse(String envVariable, List<String> args)
|
||||
throws IOException, UnmatchedQuote {
|
||||
|
||||
List<String> inArgs = new ArrayList<>();
|
||||
appendParsedEnvVariables(inArgs, envVariable);
|
||||
inArgs.addAll(args);
|
||||
List<String> newArgs = new ArrayList<>();
|
||||
appendParsedCommandArgs(newArgs, inArgs);
|
||||
return newArgs;
|
||||
}
|
||||
|
||||
public static void loadCmdFile(InputStream in, List<String> args) throws IOException {
|
||||
Reader reader = new InputStreamReader(in);
|
||||
loadCmdFileAndCloseReader(reader, args);
|
||||
}
|
||||
|
||||
private static void loadCmdFile(String name, List<String> args) throws IOException {
|
||||
Reader reader = Files.newBufferedReader(Paths.get(name), Charset.defaultCharset());
|
||||
loadCmdFileAndCloseReader(reader, args);
|
||||
}
|
||||
|
||||
private static void loadCmdFileAndCloseReader(Reader r, List<String> args) throws IOException {
|
||||
try (r) {
|
||||
Tokenizer t = new Tokenizer(r);
|
||||
String s;
|
||||
while ((s = t.nextToken()) != null) {
|
||||
@ -151,9 +186,7 @@ class CommandLine {
|
||||
switch (ch) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
while (ch == ' ' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\t'
|
||||
|| ch == '\f') {
|
||||
while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f') {
|
||||
ch = in.read();
|
||||
}
|
||||
continue;
|
||||
@ -170,8 +203,6 @@ class CommandLine {
|
||||
case 'f':
|
||||
ch = '\f';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
sb.append((char) ch);
|
||||
@ -212,4 +243,75 @@ class CommandLine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
private static void appendParsedEnvVariables(List<String> newArgs, String envVariable)
|
||||
throws UnmatchedQuote {
|
||||
|
||||
if (envVariable == null) {
|
||||
return;
|
||||
}
|
||||
String in = System.getenv(envVariable);
|
||||
if (in == null || in.trim().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final char NUL = (char)0;
|
||||
final int len = in.length();
|
||||
|
||||
int pos = 0;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char quote = NUL;
|
||||
char ch;
|
||||
|
||||
loop:
|
||||
while (pos < len) {
|
||||
ch = in.charAt(pos);
|
||||
switch (ch) {
|
||||
case '\"': case '\'':
|
||||
if (quote == NUL) {
|
||||
quote = ch;
|
||||
} else if (quote == ch) {
|
||||
quote = NUL;
|
||||
} else {
|
||||
sb.append(ch);
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case '\f': case '\n': case '\r': case '\t': case ' ':
|
||||
if (quote == NUL) {
|
||||
newArgs.add(sb.toString());
|
||||
sb.setLength(0);
|
||||
while (ch == '\f' || ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ') {
|
||||
pos++;
|
||||
if (pos >= len) {
|
||||
break loop;
|
||||
}
|
||||
ch = in.charAt(pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
default:
|
||||
sb.append(ch);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
if (sb.length() != 0) {
|
||||
newArgs.add(sb.toString());
|
||||
}
|
||||
if (quote != NUL) {
|
||||
throw new UnmatchedQuote(envVariable);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnmatchedQuote extends Exception {
|
||||
private static final long serialVersionUID = 0;
|
||||
|
||||
public final String variableName;
|
||||
|
||||
UnmatchedQuote(String variable) {
|
||||
this.variableName = variable;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,4 +30,5 @@
|
||||
*/
|
||||
module jdk.internal.opt {
|
||||
exports jdk.internal.joptsimple to jdk.jlink, jdk.jshell;
|
||||
exports jdk.internal.opt to jdk.jartool, jdk.jlink, jdk.jpackage;
|
||||
}
|
||||
|
@ -48,6 +48,8 @@
|
||||
* @since 9
|
||||
*/
|
||||
module jdk.jartool {
|
||||
requires jdk.internal.opt;
|
||||
|
||||
exports com.sun.jarsigner;
|
||||
exports jdk.security.jarsigner;
|
||||
|
||||
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.tools.jar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.FileReader;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.StreamTokenizer;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Various utility methods for processing Java tool command line arguments.
|
||||
*
|
||||
* <p><b>This is NOT part of any API supported by Oracle. If
|
||||
* you write code that depends on this, you do so at your own risk.
|
||||
* This code and its internal interfaces are subject to change or
|
||||
* deletion without notice.</b>
|
||||
*/
|
||||
public class CommandLine {
|
||||
/**
|
||||
* Process Win32-style command files for the specified command line
|
||||
* arguments and return the resulting arguments. A command file argument
|
||||
* is of the form '@file' where 'file' is the name of the file whose
|
||||
* contents are to be parsed for additional arguments. The contents of
|
||||
* the command file are parsed using StreamTokenizer and the original
|
||||
* '@file' argument replaced with the resulting tokens. Recursive command
|
||||
* files are not supported. The '@' character itself can be quoted with
|
||||
* the sequence '@@'.
|
||||
*/
|
||||
public static String[] parse(String[] args)
|
||||
throws IOException
|
||||
{
|
||||
List<String> newArgs = new ArrayList<>(args.length);
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String arg = args[i];
|
||||
if (arg.length() > 1 && arg.charAt(0) == '@') {
|
||||
arg = arg.substring(1);
|
||||
if (arg.charAt(0) == '@') {
|
||||
newArgs.add(arg);
|
||||
} else {
|
||||
loadCmdFile(arg, newArgs);
|
||||
}
|
||||
} else {
|
||||
newArgs.add(arg);
|
||||
}
|
||||
}
|
||||
return newArgs.toArray(new String[newArgs.size()]);
|
||||
}
|
||||
|
||||
private static void loadCmdFile(String name, List<String> args)
|
||||
throws IOException
|
||||
{
|
||||
Reader r = new BufferedReader(new FileReader(name));
|
||||
StreamTokenizer st = new StreamTokenizer(r);
|
||||
st.resetSyntax();
|
||||
st.wordChars(' ', 255);
|
||||
st.whitespaceChars(0, ' ');
|
||||
st.commentChar('#');
|
||||
st.quoteChar('"');
|
||||
st.quoteChar('\'');
|
||||
while (st.nextToken() != StreamTokenizer.TT_EOF) {
|
||||
args.add(st.sval);
|
||||
}
|
||||
r.close();
|
||||
}
|
||||
}
|
@ -68,6 +68,7 @@ import jdk.internal.module.ModuleInfo;
|
||||
import jdk.internal.module.ModuleInfoExtender;
|
||||
import jdk.internal.module.ModuleResolution;
|
||||
import jdk.internal.module.ModuleTarget;
|
||||
import jdk.internal.opt.CommandLine;
|
||||
import jdk.internal.util.jar.JarIndex;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
|
@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.tools.jlink.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This file was originally a copy of CommandLine.java in
|
||||
* com.sun.tools.javac.main.
|
||||
* It should track changes made to that file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Various utility methods for processing Java tool command line arguments.
|
||||
*/
|
||||
public class CommandLine {
|
||||
|
||||
static void loadCmdFile(InputStream in, List<String> args)
|
||||
throws IOException {
|
||||
try (Reader r = new InputStreamReader(in)) {
|
||||
Tokenizer t = new Tokenizer(r);
|
||||
String s;
|
||||
while ((s = t.nextToken()) != null) {
|
||||
args.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static class Tokenizer {
|
||||
private final Reader in;
|
||||
private int ch;
|
||||
|
||||
public Tokenizer(Reader in) throws IOException {
|
||||
this.in = in;
|
||||
ch = in.read();
|
||||
}
|
||||
|
||||
public String nextToken() throws IOException {
|
||||
skipWhite();
|
||||
if (ch == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char quoteChar = 0;
|
||||
|
||||
while (ch != -1) {
|
||||
switch (ch) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\f':
|
||||
if (quoteChar == 0) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append((char) ch);
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
case '\r':
|
||||
return sb.toString();
|
||||
|
||||
case '\'':
|
||||
case '"':
|
||||
if (quoteChar == 0) {
|
||||
quoteChar = (char) ch;
|
||||
} else if (quoteChar == ch) {
|
||||
quoteChar = 0;
|
||||
} else {
|
||||
sb.append((char) ch);
|
||||
}
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
if (quoteChar != 0) {
|
||||
ch = in.read();
|
||||
switch (ch) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
while (ch == ' ' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\t'
|
||||
|| ch == '\f') {
|
||||
ch = in.read();
|
||||
}
|
||||
continue;
|
||||
|
||||
case 'n':
|
||||
ch = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
ch = '\r';
|
||||
break;
|
||||
case 't':
|
||||
ch = '\t';
|
||||
break;
|
||||
case 'f':
|
||||
ch = '\f';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
sb.append((char) ch);
|
||||
break;
|
||||
|
||||
default:
|
||||
sb.append((char) ch);
|
||||
}
|
||||
|
||||
ch = in.read();
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
void skipWhite() throws IOException {
|
||||
while (ch != -1) {
|
||||
switch (ch) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\f':
|
||||
break;
|
||||
|
||||
case '#':
|
||||
ch = in.read();
|
||||
while (ch != '\n' && ch != '\r' && ch != -1) {
|
||||
ch = in.read();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
ch = in.read();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -70,6 +70,7 @@ import jdk.tools.jlink.internal.ImagePluginStack.ImageProvider;
|
||||
import jdk.tools.jlink.plugin.PluginException;
|
||||
import jdk.tools.jlink.builder.DefaultImageBuilder;
|
||||
import jdk.tools.jlink.plugin.Plugin;
|
||||
import jdk.internal.opt.CommandLine;
|
||||
import jdk.internal.module.ModulePath;
|
||||
import jdk.internal.module.ModuleResolution;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,6 +25,7 @@
|
||||
|
||||
package jdk.jpackage.main;
|
||||
|
||||
import jdk.internal.opt.CommandLine;
|
||||
import jdk.jpackage.internal.Arguments;
|
||||
import jdk.jpackage.internal.Log;
|
||||
import jdk.jpackage.internal.CLIHelp;
|
||||
|
@ -51,6 +51,7 @@
|
||||
*/
|
||||
|
||||
module jdk.jpackage {
|
||||
requires jdk.internal.opt;
|
||||
requires jdk.jlink;
|
||||
|
||||
requires java.desktop;
|
||||
|
Loading…
x
Reference in New Issue
Block a user