8236919: Refactor com.sun.tools.javac.main.CommandLine into a reusable module for other JDK tools

Reviewed-by: jjg
This commit is contained in:
Christian Stein 2022-11-28 17:33:59 +00:00
parent a249a52501
commit a80552e1e1
9 changed files with 143 additions and 291 deletions

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -48,6 +48,8 @@
* @since 9
*/
module jdk.jartool {
requires jdk.internal.opt;
exports com.sun.jarsigner;
exports jdk.security.jarsigner;

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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();
}
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -51,6 +51,7 @@
*/
module jdk.jpackage {
requires jdk.internal.opt;
requires jdk.jlink;
requires java.desktop;