1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @bug 8027634 8210810 27 * @summary Verify syntax of argument file 28 * @build TestHelper 29 * @run main ArgFileSyntax 30 */ 31 import java.io.File; 32 import java.io.IOException; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.Collections; 36 import java.util.HashMap; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.regex.Matcher; 40 import java.util.regex.Pattern; 41 42 public class ArgFileSyntax extends TestHelper { 43 // Buffer size in args.c readArgFile() method 44 private static final int ARG_FILE_PARSER_BUF_SIZE = 4096; 45 createArgFile(List<String> lines)46 private File createArgFile(List<String> lines) throws IOException { 47 File argFile = new File("argfile"); 48 argFile.delete(); 49 createAFile(argFile, lines); 50 return argFile; 51 } 52 verifyOutput(List<String> args, TestResult tr)53 private void verifyOutput(List<String> args, TestResult tr) { 54 if (args.isEmpty()) { 55 return; 56 } 57 58 int i = 1; 59 for (String x : args) { 60 tr.matches(".*argv\\[" + i + "\\] = " + Pattern.quote(x) + ".*"); 61 i++; 62 } 63 if (! tr.testStatus) { 64 System.out.println(tr); 65 throw new RuntimeException("test fails"); 66 } 67 } 68 69 // arg file content, expected options 70 static String[] testCases[][] = { 71 { // empty file 72 {}, {} 73 }, 74 { // comments and # inside quote 75 { "# a couple of -X flags", 76 "-Xmx32m", 77 "-XshowSettings #inline comment", 78 "-Dpound.in.quote=\"This property contains #.\"", 79 "# add -version", 80 "-version", 81 "# trail comment" 82 }, 83 { "-Xmx32m", 84 "-XshowSettings", 85 "-Dpound.in.quote=This property contains #.", 86 "-version" 87 } 88 }, 89 { // open quote with continuation directive 90 // multiple options in a line 91 { "-cp \"c:\\\\java lib\\\\all;\\", 92 " c:\\\\lib\"", 93 "-Xmx32m -XshowSettings", 94 "-version" 95 }, 96 { "-cp", 97 "c:\\java lib\\all;c:\\lib", 98 "-Xmx32m", 99 "-XshowSettings", 100 "-version" 101 } 102 }, 103 { // no continuation on open quote 104 // multiple lines in a property 105 { "-cp \"c:\\\\open quote\\\\all;", 106 " # c:\\\\lib\"", 107 "-Dmultiple.lines=\"line 1\\nline 2\\n\\rline 3\"", 108 "-Dopen.quote=\"Open quote to EOL", 109 "-Dcontinue.with.leadingWS=\"Continue with\\", 110 " \\ leading WS.", 111 "-Dcontinue.without.leadingWS=\"Continue without \\", 112 " leading WS.", 113 "-Descape.seq=\"escaped chars: \\\"\\a\\b\\c\\f\\t\\v\\9\\6\\23\\82\\28\\377\\477\\278\\287\\n\"", 114 "-version" 115 }, 116 { "-cp", 117 "c:\\open quote\\all;", 118 "-Dmultiple.lines=line 1", 119 // line 2 and line 3 shoule be in output, but not as arg[x]= 120 "-Dopen.quote=Open quote to EOL", 121 "-Dcontinue.with.leadingWS=Continue with leading WS.", 122 "-Dcontinue.without.leadingWS=Continue without leading WS.", 123 // cannot verify \n and \r as that break output lines 124 "-Descape.seq=escaped chars: \"abc\f\tv96238228377477278287", 125 "-version" 126 } 127 }, 128 { // No need to escape if not in quote 129 // also quote part of a token 130 { "-cp c:\\\"partial quote\"\\all", 131 "-Xmx32m -XshowSettings", 132 "-version" 133 }, 134 { "-cp", 135 "c:\\partial quote\\all", 136 "-Xmx32m", 137 "-XshowSettings", 138 "-version" 139 } 140 }, 141 { // No recursive expansion 142 { "-Xmx32m", 143 "-cp", 144 " # @cpfile should remains @cpfile", 145 "@cpfile", 146 "-version" 147 }, 148 { "-Xmx32m", 149 "-cp", 150 "@cpfile", 151 "-version" 152 } 153 }, 154 { // Mix quotation 155 { "-Dsingle.in.double=\"Mix 'single' in double\"", 156 "-Ddouble.in.single='Mix \"double\" in single'", 157 "-Dsingle.in.single='Escape \\\'single\\\' in single'", 158 "-Ddouble.in.double=\"Escape \\\"double\\\" in double\"" 159 }, 160 { "-Dsingle.in.double=Mix 'single' in double", 161 "-Ddouble.in.single=Mix \"double\" in single", 162 "-Dsingle.in.single=Escape 'single' in single", 163 "-Ddouble.in.double=Escape \"double\" in double" 164 }, 165 }, 166 { // \t\f as whitespace and in escape 167 { "-Xmx32m\t-Xint\f-version", 168 "-Dcontinue.with.leadingws=\"Line1\\", 169 " \t\fcontinue with \\f<ff> and \\t<tab>" 170 }, 171 { "-Xmx32m", 172 "-Xint", 173 "-version", 174 "-Dcontinue.with.leadingws=Line1continue with \f<ff> and \t<tab>" 175 } 176 } 177 }; 178 loadCases()179 public List<List<List<String>>> loadCases() { 180 List<List<List<String>>> rv = new ArrayList<>(); 181 for (String[][] testCaseArray: testCases) { 182 List<List<String>> testCase = new ArrayList<>(2); 183 testCase.add(Arrays.asList(testCaseArray[0])); 184 testCase.add(Arrays.asList(testCaseArray[1])); 185 rv.add(testCase); 186 } 187 188 // long lines 189 String bag = "-Dgarbage="; 190 String ver = "-version"; 191 // a token 8192 long 192 char[] data = new char[2*ARG_FILE_PARSER_BUF_SIZE - bag.length()]; 193 Arrays.fill(data, 'O'); 194 List<String> scratch = new ArrayList<>(); 195 scratch.add("-Xmx32m"); 196 scratch.add(bag + String.valueOf(data)); 197 scratch.add(ver); 198 rv.add(Collections.nCopies(2, scratch)); 199 200 data = new char[2*ARG_FILE_PARSER_BUF_SIZE + 1024]; 201 Arrays.fill(data, 'O'); 202 scratch = new ArrayList<>(); 203 scratch.add(bag + String.valueOf(data)); 204 scratch.add(ver); 205 rv.add(Collections.nCopies(2, scratch)); 206 207 // 8210810: position escaping character at boundary 208 // reserve space for quote and backslash 209 data = new char[ARG_FILE_PARSER_BUF_SIZE - bag.length() - 2]; 210 Arrays.fill(data, 'O'); 211 scratch = new ArrayList<>(); 212 String filling = String.valueOf(data); 213 scratch.add(bag + "'" + filling + "\\\\aaa\\\\'"); 214 scratch.add(ver); 215 rv.add(List.of(scratch, List.of(bag + filling + "\\aaa\\", ver))); 216 217 return rv; 218 } 219 220 // ensure the arguments in the file are read in correctly verifyParsing(List<String> lines, List<String> args)221 private void verifyParsing(List<String> lines, List<String> args) throws IOException { 222 File argFile = createArgFile(lines); 223 String fname = "@" + argFile.getName(); 224 Map<String, String> env = new HashMap<>(); 225 env.put(JLDEBUG_KEY, "true"); 226 227 TestResult tr; 228 if (args.contains("-version")) { 229 tr = doExec(env, javaCmd, fname); 230 } else { 231 tr = doExec(env, javaCmd, fname, "-version"); 232 } 233 tr.checkPositive(); 234 verifyOutput(args, tr); 235 236 String lastArg = args.contains("-version") ? "-Dlast.arg" : "-version"; 237 tr = doExec(env, javaCmd, "-Xint", fname, lastArg); 238 List<String> scratch = new ArrayList<>(); 239 scratch.add("-Xint"); 240 scratch.addAll(args); 241 scratch.add(lastArg); 242 verifyOutput(scratch, tr); 243 244 argFile.delete(); 245 } 246 247 @Test testSyntax()248 public void testSyntax() throws IOException { 249 List<List<List<String>>> allcases = loadCases(); 250 for (List<List<String>> test: allcases) { 251 verifyParsing(test.get(0), test.get(1)); 252 } 253 } 254 255 @Test badCases()256 public void badCases() throws IOException { 257 List<String> lines = Arrays.asList( 258 "-Dno.escape=\"Forgot to escape backslash\\\" -version"); 259 File argFile = createArgFile(lines); 260 String fname = "@" + argFile.getName(); 261 Map<String, String> env = new HashMap<>(); 262 env.put(JLDEBUG_KEY, "true"); 263 264 TestResult tr = doExec(env, javaCmd, fname); 265 tr.contains("argv[1] = -Dno.escape=Forgot to escape backslash\" -version"); 266 tr.checkNegative(); 267 if (!tr.testStatus) { 268 System.out.println(tr); 269 throw new RuntimeException("test fails"); 270 } 271 argFile.delete(); 272 } 273 main(String... args)274 public static void main(String... args) throws Exception { 275 ArgFileSyntax a = new ArgFileSyntax(); 276 a.run(args); 277 if (testExitValue > 0) { 278 System.out.println("Total of " + testExitValue + " failed"); 279 System.exit(1); 280 } else { 281 System.out.println("All tests pass"); 282 } 283 } 284 } 285