143 } |
143 } |
144 } |
144 } |
145 |
145 |
146 } |
146 } |
147 |
147 |
|
148 // We guarantee the only command file execution for implicit [cmd.exe] run. |
|
149 // http://technet.microsoft.com/en-us/library/bb490954.aspx |
|
150 private static final char CMD_BAT_ESCAPE[] = {' ', '\t', '<', '>', '&', '|', '^'}; |
|
151 private static final char WIN32_EXECUTABLE_ESCAPE[] = {' ', '\t', '<', '>'}; |
|
152 |
|
153 private static boolean isQuoted(boolean noQuotesInside, String arg, |
|
154 String errorMessage) { |
|
155 int lastPos = arg.length() - 1; |
|
156 if (lastPos >=1 && arg.charAt(0) == '"' && arg.charAt(lastPos) == '"') { |
|
157 // The argument has already been quoted. |
|
158 if (noQuotesInside) { |
|
159 if (arg.indexOf('"', 1) != lastPos) { |
|
160 // There is ["] inside. |
|
161 throw new IllegalArgumentException(errorMessage); |
|
162 } |
|
163 } |
|
164 return true; |
|
165 } |
|
166 if (noQuotesInside) { |
|
167 if (arg.indexOf('"') >= 0) { |
|
168 // There is ["] inside. |
|
169 throw new IllegalArgumentException(errorMessage); |
|
170 } |
|
171 } |
|
172 return false; |
|
173 } |
|
174 |
|
175 private static boolean needsEscaping(boolean isCmdFile, String arg) { |
|
176 // Switch off MS heuristic for internal ["]. |
|
177 // Please, use the explicit [cmd.exe] call |
|
178 // if you need the internal ["]. |
|
179 // Example: "cmd.exe", "/C", "Extended_MS_Syntax" |
|
180 |
|
181 // For [.exe] or [.com] file the unpaired/internal ["] |
|
182 // in the argument is not a problem. |
|
183 boolean argIsQuoted = isQuoted(isCmdFile, arg, |
|
184 "Argument has embedded quote, use the explicit CMD.EXE call."); |
|
185 |
|
186 if (!argIsQuoted) { |
|
187 char testEscape[] = isCmdFile |
|
188 ? CMD_BAT_ESCAPE |
|
189 : WIN32_EXECUTABLE_ESCAPE; |
|
190 for (int i = 0; i < testEscape.length; ++i) { |
|
191 if (arg.indexOf(testEscape[i]) >= 0) { |
|
192 return true; |
|
193 } |
|
194 } |
|
195 } |
|
196 return false; |
|
197 } |
|
198 |
|
199 private static String getExecutablePath(String path) |
|
200 throws IOException |
|
201 { |
|
202 boolean pathIsQuoted = isQuoted(true, path, |
|
203 "Executable name has embedded quote, split the arguments"); |
|
204 |
|
205 // Win32 CreateProcess requires path to be normalized |
|
206 File fileToRun = new File(pathIsQuoted |
|
207 ? path.substring(1, path.length() - 1) |
|
208 : path); |
|
209 |
|
210 // From the [CreateProcess] function documentation: |
|
211 // |
|
212 // "If the file name does not contain an extension, .exe is appended. |
|
213 // Therefore, if the file name extension is .com, this parameter |
|
214 // must include the .com extension. If the file name ends in |
|
215 // a period (.) with no extension, or if the file name contains a path, |
|
216 // .exe is not appended." |
|
217 // |
|
218 // "If the file name !does not contain a directory path!, |
|
219 // the system searches for the executable file in the following |
|
220 // sequence:..." |
|
221 // |
|
222 // In practice ANY non-existent path is extended by [.exe] extension |
|
223 // in the [CreateProcess] funcion with the only exception: |
|
224 // the path ends by (.) |
|
225 |
|
226 return fileToRun.getPath(); |
|
227 } |
|
228 |
|
229 |
148 private long handle = 0; |
230 private long handle = 0; |
149 private OutputStream stdin_stream; |
231 private OutputStream stdin_stream; |
150 private InputStream stdout_stream; |
232 private InputStream stdout_stream; |
151 private InputStream stderr_stream; |
233 private InputStream stderr_stream; |
152 |
234 |
155 final String path, |
237 final String path, |
156 final long[] stdHandles, |
238 final long[] stdHandles, |
157 final boolean redirectErrorStream) |
239 final boolean redirectErrorStream) |
158 throws IOException |
240 throws IOException |
159 { |
241 { |
160 // Win32 CreateProcess requires cmd[0] to be normalized |
242 // The [executablePath] is not quoted for any case. |
161 cmd[0] = new File(cmd[0]).getPath(); |
243 String executablePath = getExecutablePath(cmd[0]); |
|
244 |
|
245 // We need to extend the argument verification procedure |
|
246 // to guarantee the only command file execution for implicit [cmd.exe] |
|
247 // run. |
|
248 String upPath = executablePath.toUpperCase(); |
|
249 boolean isCmdFile = (upPath.endsWith(".CMD") || upPath.endsWith(".BAT")); |
162 |
250 |
163 StringBuilder cmdbuf = new StringBuilder(80); |
251 StringBuilder cmdbuf = new StringBuilder(80); |
164 for (int i = 0; i < cmd.length; i++) { |
252 |
165 if (i > 0) { |
253 // Quotation protects from interpretation of the [path] argument as |
166 cmdbuf.append(' '); |
254 // start of longer path with spaces. Quotation has no influence to |
167 } |
255 // [.exe] extension heuristic. |
|
256 cmdbuf.append('"'); |
|
257 cmdbuf.append(executablePath); |
|
258 cmdbuf.append('"'); |
|
259 |
|
260 for (int i = 1; i < cmd.length; i++) { |
|
261 cmdbuf.append(' '); |
168 String s = cmd[i]; |
262 String s = cmd[i]; |
169 if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0) { |
263 if (needsEscaping(isCmdFile, s)) { |
170 if (s.charAt(0) != '"') { |
264 cmdbuf.append('"'); |
171 cmdbuf.append('"'); |
265 cmdbuf.append(s); |
172 cmdbuf.append(s); |
266 |
173 if (s.endsWith("\\")) { |
267 // The code protects the [java.exe] and console command line |
174 cmdbuf.append("\\"); |
268 // parser, that interprets the [\"] combination as an escape |
175 } |
269 // sequence for the ["] char. |
176 cmdbuf.append('"'); |
270 // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx |
177 } else if (s.endsWith("\"")) { |
271 // |
178 /* The argument has already been quoted. */ |
272 // If the argument is an FS path, doubling of the tail [\] |
179 cmdbuf.append(s); |
273 // char is not a problem for non-console applications. |
180 } else { |
274 // |
181 /* Unmatched quote for the argument. */ |
275 // The [\"] sequence is not an escape sequence for the [cmd.exe] |
182 throw new IllegalArgumentException(); |
276 // command line parser. The case of the [""] tail escape |
183 } |
277 // sequence could not be realized due to the argument validation |
|
278 // procedure. |
|
279 if (!isCmdFile && s.endsWith("\\")) { |
|
280 cmdbuf.append('\\'); |
|
281 } |
|
282 cmdbuf.append('"'); |
184 } else { |
283 } else { |
185 cmdbuf.append(s); |
284 cmdbuf.append(s); |
186 } |
285 } |
187 } |
286 } |
188 String cmdstr = cmdbuf.toString(); |
287 String cmdstr = cmdbuf.toString(); |