6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.jpackage.main;
27
28 import java.io.IOException;
29 import java.io.Reader;
30 import java.nio.charset.Charset;
31 import java.nio.file.Files;
32 import java.nio.file.Paths;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.List;
36
37 /**
38 * Various utility methods for processing Java tool command line arguments.
39 *
40 * <p><b>This is NOT part of any supported API.
41 * If you write code that depends on this, you do so at your own risk.
42 * This code and its internal interfaces are subject to change or
43 * deletion without notice.</b>
44 */
45 public class CommandLine {
46 /**
47 * Process Win32-style command files for the specified command line
48 * arguments and return the resulting arguments. A command file argument
49 * is of the form '@file' where 'file' is the name of the file whose
50 * contents are to be parsed for additional arguments. The contents of
51 * the command file are parsed using StreamTokenizer and the original
52 * '@file' argument replaced with the resulting tokens. Recursive command
53 * files are not supported. The '@' character itself can be quoted with
54 * the sequence '@@'.
55 * @param args the arguments that may contain @files
56 * @return the arguments, with @files expanded
57 * @throws IOException if there is a problem reading any of the @files
58 */
59 public static String[] parse(String[] args) throws IOException {
60 List<String> newArgs = new ArrayList<>();
61 appendParsedCommandArgs(newArgs, Arrays.asList(args));
62 return newArgs.toArray(new String[newArgs.size()]);
63 }
64
65 private static void appendParsedCommandArgs(List<String> newArgs, List<String> args) throws IOException {
66 for (String arg : args) {
67 if (arg.length() > 1 && arg.charAt(0) == '@') {
68 arg = arg.substring(1);
69 if (arg.charAt(0) == '@') {
70 newArgs.add(arg);
71 } else {
72 loadCmdFile(arg, newArgs);
73 }
74 } else {
75 newArgs.add(arg);
76 }
77 }
78 }
79
80 /**
81 * Process the given environment variable and appends any Win32-style
82 * command files for the specified command line arguments and return
83 * the resulting arguments. A command file argument
84 * is of the form '@file' where 'file' is the name of the file whose
85 * contents are to be parsed for additional arguments. The contents of
86 * the command file are parsed using StreamTokenizer and the original
87 * '@file' argument replaced with the resulting tokens. Recursive command
88 * files are not supported. The '@' character itself can be quoted with
89 * the sequence '@@'.
90 * @param envVariable the env variable to process
91 * @param args the arguments that may contain @files
92 * @return the arguments, with environment variable's content and expansion of @files
93 * @throws IOException if there is a problem reading any of the @files
94 * @throws com.sun.tools.javac.main.CommandLine.UnmatchedQuote
95 */
96 public static List<String> parse(String envVariable, List<String> args)
97 throws IOException, UnmatchedQuote {
98
99 List<String> inArgs = new ArrayList<>();
100 appendParsedEnvVariables(inArgs, envVariable);
101 inArgs.addAll(args);
102 List<String> newArgs = new ArrayList<>();
103 appendParsedCommandArgs(newArgs, inArgs);
104 return newArgs;
105 }
106
107 /**
108 * Process the given environment variable and appends any Win32-style
109 * command files for the specified command line arguments and return
110 * the resulting arguments. A command file argument
111 * is of the form '@file' where 'file' is the name of the file whose
112 * contents are to be parsed for additional arguments. The contents of
113 * the command file are parsed using StreamTokenizer and the original
114 * '@file' argument replaced with the resulting tokens. Recursive command
115 * files are not supported. The '@' character itself can be quoted with
116 * the sequence '@@'.
117 * @param envVariable the env variable to process
118 * @param args the arguments that may contain @files
119 * @return the arguments, with environment variable's content and expansion of @files
120 * @throws IOException if there is a problem reading any of the @files
121 * @throws com.sun.tools.javac.main.CommandLine.UnmatchedQuote
122 */
123 public static String[] parse(String envVariable, String[] args) throws IOException, UnmatchedQuote {
124 List<String> out = parse(envVariable, Arrays.asList(args));
125 return out.toArray(new String[out.size()]);
126 }
127
128 private static void loadCmdFile(String name, List<String> args) throws IOException {
129 try (Reader r = Files.newBufferedReader(Paths.get(name), Charset.defaultCharset())) {
130 Tokenizer t = new Tokenizer(r);
131 String s;
132 while ((s = t.nextToken()) != null) {
133 args.add(s);
134 }
135 }
136 }
137
138 public static class Tokenizer {
139 private final Reader in;
140 private int ch;
141
142 public Tokenizer(Reader in) throws IOException {
143 this.in = in;
144 ch = in.read();
145 }
146
147 public String nextToken() throws IOException {
148 skipWhite();
149 if (ch == -1) {
168 case '\r':
169 return sb.toString();
170
171 case '\'':
172 case '"':
173 if (quoteChar == 0) {
174 quoteChar = (char) ch;
175 } else if (quoteChar == ch) {
176 quoteChar = 0;
177 } else {
178 sb.append((char) ch);
179 }
180 break;
181
182 case '\\':
183 if (quoteChar != 0) {
184 ch = in.read();
185 switch (ch) {
186 case '\n':
187 case '\r':
188 while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f') {
189 ch = in.read();
190 }
191 continue;
192
193 case 'n':
194 ch = '\n';
195 break;
196 case 'r':
197 ch = '\r';
198 break;
199 case 't':
200 ch = '\t';
201 break;
202 case 'f':
203 ch = '\f';
204 break;
205 }
206 }
207 sb.append((char) ch);
208 break;
223 case ' ':
224 case '\t':
225 case '\n':
226 case '\r':
227 case '\f':
228 break;
229
230 case '#':
231 ch = in.read();
232 while (ch != '\n' && ch != '\r' && ch != -1) {
233 ch = in.read();
234 }
235 break;
236
237 default:
238 return;
239 }
240
241 ch = in.read();
242 }
243 }
244 }
245
246 @SuppressWarnings("fallthrough")
247 private static void appendParsedEnvVariables(List<String> newArgs, String envVariable)
248 throws UnmatchedQuote {
249
250 if (envVariable == null) {
251 return;
252 }
253 String in = System.getenv(envVariable);
254 if (in == null || in.trim().isEmpty()) {
255 return;
256 }
257
258 final char NUL = (char)0;
259 final int len = in.length();
260
261 int pos = 0;
262 StringBuilder sb = new StringBuilder();
263 char quote = NUL;
264 char ch;
265
266 loop:
267 while (pos < len) {
268 ch = in.charAt(pos);
269 switch (ch) {
270 case '\"': case '\'':
271 if (quote == NUL) {
272 quote = ch;
273 } else if (quote == ch) {
274 quote = NUL;
275 } else {
276 sb.append(ch);
277 }
278 pos++;
279 break;
280 case '\f': case '\n': case '\r': case '\t': case ' ':
281 if (quote == NUL) {
282 newArgs.add(sb.toString());
283 sb.setLength(0);
284 while (ch == '\f' || ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ') {
285 pos++;
286 if (pos >= len) {
287 break loop;
288 }
289 ch = in.charAt(pos);
290 }
291 break;
292 }
293 // fall through
294 default:
295 sb.append(ch);
296 pos++;
297 }
298 }
299 if (sb.length() != 0) {
300 newArgs.add(sb.toString());
301 }
302 if (quote != NUL) {
303 throw new UnmatchedQuote(envVariable);
304 }
305 }
306
307 public static class UnmatchedQuote extends Exception {
308 private static final long serialVersionUID = 0;
309
310 public final String variableName;
311
312 UnmatchedQuote(String variable) {
313 this.variableName = variable;
314 }
315 }
316 }
|
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.incubator.jpackage.main;
27
28 import java.io.FileNotFoundException;
29 import java.io.IOException;
30 import java.io.File;
31 import java.io.Reader;
32 import java.nio.charset.Charset;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.nio.file.Paths;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.List;
39
40 /**
41 * This file was originally a copy of CommandLine.java in
42 * com.sun.tools.javac.main.
43 * It should track changes made to that file.
44 */
45
46 /**
47 * Various utility methods for processing Java tool command line arguments.
48 *
49 * <p><b>This is NOT part of any supported API.
50 * If you write code that depends on this, you do so at your own risk.
51 * This code and its internal interfaces are subject to change or
52 * deletion without notice.</b>
53 */
54 class CommandLine {
55 /**
56 * Process Win32-style command files for the specified command line
57 * arguments and return the resulting arguments. A command file argument
58 * is of the form '@file' where 'file' is the name of the file whose
59 * contents are to be parsed for additional arguments. The contents of
60 * the command file are parsed using StreamTokenizer and the original
61 * '@file' argument replaced with the resulting tokens. Recursive command
62 * files are not supported. The '@' character itself can be quoted with
63 * the sequence '@@'.
64 * @param args the arguments that may contain @files
65 * @return the arguments, with @files expanded
66 * @throws IOException if there is a problem reading any of the @files
67 */
68 public static String[] parse(String[] args) throws IOException {
69 List<String> newArgs = new ArrayList<>();
70 appendParsedCommandArgs(newArgs, Arrays.asList(args));
71 return newArgs.toArray(new String[newArgs.size()]);
72 }
73
74 private static void appendParsedCommandArgs(List<String> newArgs,
75 List<String> args) throws IOException {
76 for (String arg : args) {
77 if (arg.length() > 1 && arg.charAt(0) == '@') {
78 arg = arg.substring(1);
79 if (arg.charAt(0) == '@') {
80 newArgs.add(arg);
81 } else {
82 loadCmdFile(arg, newArgs);
83 }
84 } else {
85 newArgs.add(arg);
86 }
87 }
88 }
89
90 private static void loadCmdFile(String name, List<String> args)
91 throws IOException {
92 if (!Files.isReadable(Path.of(name))) {
93 throw new FileNotFoundException(name);
94 }
95 try (Reader r = Files.newBufferedReader(Paths.get(name),
96 Charset.defaultCharset())) {
97 Tokenizer t = new Tokenizer(r);
98 String s;
99 while ((s = t.nextToken()) != null) {
100 args.add(s);
101 }
102 }
103 }
104
105 public static class Tokenizer {
106 private final Reader in;
107 private int ch;
108
109 public Tokenizer(Reader in) throws IOException {
110 this.in = in;
111 ch = in.read();
112 }
113
114 public String nextToken() throws IOException {
115 skipWhite();
116 if (ch == -1) {
135 case '\r':
136 return sb.toString();
137
138 case '\'':
139 case '"':
140 if (quoteChar == 0) {
141 quoteChar = (char) ch;
142 } else if (quoteChar == ch) {
143 quoteChar = 0;
144 } else {
145 sb.append((char) ch);
146 }
147 break;
148
149 case '\\':
150 if (quoteChar != 0) {
151 ch = in.read();
152 switch (ch) {
153 case '\n':
154 case '\r':
155 while (ch == ' ' || ch == '\n'
156 || ch == '\r' || ch == '\t'
157 || ch == '\f') {
158 ch = in.read();
159 }
160 continue;
161
162 case 'n':
163 ch = '\n';
164 break;
165 case 'r':
166 ch = '\r';
167 break;
168 case 't':
169 ch = '\t';
170 break;
171 case 'f':
172 ch = '\f';
173 break;
174 }
175 }
176 sb.append((char) ch);
177 break;
192 case ' ':
193 case '\t':
194 case '\n':
195 case '\r':
196 case '\f':
197 break;
198
199 case '#':
200 ch = in.read();
201 while (ch != '\n' && ch != '\r' && ch != -1) {
202 ch = in.read();
203 }
204 break;
205
206 default:
207 return;
208 }
209
210 ch = in.read();
211 }
212 }
213 }
214 }
|