1/* Execute a Java program. 2 Copyright (C) 2001-2003, 2006-2007 Free Software Foundation, Inc. 3 Written by Bruno Haible <haible@clisp.cons.org>, 2001. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18#include <config.h> 19#include <alloca.h> 20 21/* Specification. */ 22#include "javaexec.h" 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27 28#include "execute.h" 29#include "classpath.h" 30#include "xsetenv.h" 31#include "sh-quote.h" 32#include "filename.h" 33#include "xalloc.h" 34#include "xmalloca.h" 35#include "error.h" 36#include "gettext.h" 37 38#define _(str) gettext (str) 39 40 41/* Survey of Java virtual machines. 42 43 A = does it work without CLASSPATH being set 44 B = does it work with CLASSPATH being set to empty 45 C = option to set CLASSPATH, other than setting it in the environment 46 T = test for presence 47 48 Program from A B C T 49 50 $JAVA unknown N Y n/a true 51 gij GCC 3.0 Y Y n/a gij --version >/dev/null 52 java JDK 1.1.8 Y Y -classpath P java -version 2>/dev/null 53 jre JDK 1.1.8 N Y -classpath P jre 2>/dev/null; test $? = 1 54 java JDK 1.3.0 Y Y -classpath P java -version 2>/dev/null 55 jview MS IE Y Y -cp P jview -? >nul; %errorlevel% = 1 56 57 The CLASSPATH is a colon separated list of pathnames. (On Windows: a 58 semicolon separated list of pathnames.) 59 60 We try the Java virtual machines in the following order: 61 1. getenv ("JAVA"), because the user must be able to override our 62 preferences, 63 2. "gij", because it is a completely free JVM, 64 3. "java", because it is a standard JVM, 65 4. "jre", comes last because it requires a CLASSPATH environment variable, 66 5. "jview", on Windows only, because it is frequently installed. 67 68 We unset the JAVA_HOME environment variable, because a wrong setting of 69 this variable can confuse the JDK's javac. 70 */ 71 72bool 73execute_java_class (const char *class_name, 74 const char * const *classpaths, 75 unsigned int classpaths_count, 76 bool use_minimal_classpath, 77 const char *exe_dir, 78 const char * const *args, 79 bool verbose, bool quiet, 80 execute_fn *executer, void *private_data) 81{ 82 bool err = false; 83 unsigned int nargs; 84 char *old_JAVA_HOME; 85 86 /* Count args. */ 87 { 88 const char * const *arg; 89 90 for (nargs = 0, arg = args; *arg != NULL; nargs++, arg++) 91 ; 92 } 93 94 /* First, try a class compiled to a native code executable. */ 95 if (exe_dir != NULL) 96 { 97 char *exe_pathname = concatenated_filename (exe_dir, class_name, EXEEXT); 98 char *old_classpath; 99 char **argv = (char **) xmalloca ((1 + nargs + 1) * sizeof (char *)); 100 unsigned int i; 101 102 /* Set CLASSPATH. */ 103 old_classpath = 104 set_classpath (classpaths, classpaths_count, use_minimal_classpath, 105 verbose); 106 107 argv[0] = exe_pathname; 108 for (i = 0; i <= nargs; i++) 109 argv[1 + i] = (char *) args[i]; 110 111 if (verbose) 112 { 113 char *command = shell_quote_argv (argv); 114 printf ("%s\n", command); 115 free (command); 116 } 117 118 err = executer (class_name, exe_pathname, argv, private_data); 119 120 /* Reset CLASSPATH. */ 121 reset_classpath (old_classpath); 122 123 freea (argv); 124 125 goto done1; 126 } 127 128 { 129 const char *java = getenv ("JAVA"); 130 if (java != NULL && java[0] != '\0') 131 { 132 /* Because $JAVA may consist of a command and options, we use the 133 shell. Because $JAVA has been set by the user, we leave all 134 all environment variables in place, including JAVA_HOME, and 135 we don't erase the user's CLASSPATH. */ 136 char *old_classpath; 137 unsigned int command_length; 138 char *command; 139 char *argv[4]; 140 const char * const *arg; 141 char *p; 142 143 /* Set CLASSPATH. */ 144 old_classpath = 145 set_classpath (classpaths, classpaths_count, false, 146 verbose); 147 148 command_length = strlen (java); 149 command_length += 1 + shell_quote_length (class_name); 150 for (arg = args; *arg != NULL; arg++) 151 command_length += 1 + shell_quote_length (*arg); 152 command_length += 1; 153 154 command = (char *) xmalloca (command_length); 155 p = command; 156 /* Don't shell_quote $JAVA, because it may consist of a command 157 and options. */ 158 memcpy (p, java, strlen (java)); 159 p += strlen (java); 160 *p++ = ' '; 161 p = shell_quote_copy (p, class_name); 162 for (arg = args; *arg != NULL; arg++) 163 { 164 *p++ = ' '; 165 p = shell_quote_copy (p, *arg); 166 } 167 *p++ = '\0'; 168 /* Ensure command_length was correctly calculated. */ 169 if (p - command > command_length) 170 abort (); 171 172 if (verbose) 173 printf ("%s\n", command); 174 175 argv[0] = "/bin/sh"; 176 argv[1] = "-c"; 177 argv[2] = command; 178 argv[3] = NULL; 179 err = executer (java, "/bin/sh", argv, private_data); 180 181 freea (command); 182 183 /* Reset CLASSPATH. */ 184 reset_classpath (old_classpath); 185 186 goto done1; 187 } 188 } 189 190 /* Unset the JAVA_HOME environment variable. */ 191 old_JAVA_HOME = getenv ("JAVA_HOME"); 192 if (old_JAVA_HOME != NULL) 193 { 194 old_JAVA_HOME = xstrdup (old_JAVA_HOME); 195 unsetenv ("JAVA_HOME"); 196 } 197 198 { 199 static bool gij_tested; 200 static bool gij_present; 201 202 if (!gij_tested) 203 { 204 /* Test for presence of gij: "gij --version > /dev/null" */ 205 char *argv[3]; 206 int exitstatus; 207 208 argv[0] = "gij"; 209 argv[1] = "--version"; 210 argv[2] = NULL; 211 exitstatus = execute ("gij", "gij", argv, false, false, true, true, 212 true, false); 213 gij_present = (exitstatus == 0); 214 gij_tested = true; 215 } 216 217 if (gij_present) 218 { 219 char *old_classpath; 220 char **argv = (char **) xmalloca ((2 + nargs + 1) * sizeof (char *)); 221 unsigned int i; 222 223 /* Set CLASSPATH. */ 224 old_classpath = 225 set_classpath (classpaths, classpaths_count, use_minimal_classpath, 226 verbose); 227 228 argv[0] = "gij"; 229 argv[1] = (char *) class_name; 230 for (i = 0; i <= nargs; i++) 231 argv[2 + i] = (char *) args[i]; 232 233 if (verbose) 234 { 235 char *command = shell_quote_argv (argv); 236 printf ("%s\n", command); 237 free (command); 238 } 239 240 err = executer ("gij", "gij", argv, private_data); 241 242 /* Reset CLASSPATH. */ 243 reset_classpath (old_classpath); 244 245 freea (argv); 246 247 goto done2; 248 } 249 } 250 251 { 252 static bool java_tested; 253 static bool java_present; 254 255 if (!java_tested) 256 { 257 /* Test for presence of java: "java -version 2> /dev/null" */ 258 char *argv[3]; 259 int exitstatus; 260 261 argv[0] = "java"; 262 argv[1] = "-version"; 263 argv[2] = NULL; 264 exitstatus = execute ("java", "java", argv, false, false, true, true, 265 true, false); 266 java_present = (exitstatus == 0); 267 java_tested = true; 268 } 269 270 if (java_present) 271 { 272 char *old_classpath; 273 char **argv = (char **) xmalloca ((2 + nargs + 1) * sizeof (char *)); 274 unsigned int i; 275 276 /* Set CLASSPATH. We don't use the "-classpath ..." option because 277 in JDK 1.1.x its argument should also contain the JDK's classes.zip, 278 but we don't know its location. (In JDK 1.3.0 it would work.) */ 279 old_classpath = 280 set_classpath (classpaths, classpaths_count, use_minimal_classpath, 281 verbose); 282 283 argv[0] = "java"; 284 argv[1] = (char *) class_name; 285 for (i = 0; i <= nargs; i++) 286 argv[2 + i] = (char *) args[i]; 287 288 if (verbose) 289 { 290 char *command = shell_quote_argv (argv); 291 printf ("%s\n", command); 292 free (command); 293 } 294 295 err = executer ("java", "java", argv, private_data); 296 297 /* Reset CLASSPATH. */ 298 reset_classpath (old_classpath); 299 300 freea (argv); 301 302 goto done2; 303 } 304 } 305 306 { 307 static bool jre_tested; 308 static bool jre_present; 309 310 if (!jre_tested) 311 { 312 /* Test for presence of jre: "jre 2> /dev/null ; test $? = 1" */ 313 char *argv[2]; 314 int exitstatus; 315 316 argv[0] = "jre"; 317 argv[1] = NULL; 318 exitstatus = execute ("jre", "jre", argv, false, false, true, true, 319 true, false); 320 jre_present = (exitstatus == 0 || exitstatus == 1); 321 jre_tested = true; 322 } 323 324 if (jre_present) 325 { 326 char *old_classpath; 327 char **argv = (char **) xmalloca ((2 + nargs + 1) * sizeof (char *)); 328 unsigned int i; 329 330 /* Set CLASSPATH. We don't use the "-classpath ..." option because 331 in JDK 1.1.x its argument should also contain the JDK's classes.zip, 332 but we don't know its location. */ 333 old_classpath = 334 set_classpath (classpaths, classpaths_count, use_minimal_classpath, 335 verbose); 336 337 argv[0] = "jre"; 338 argv[1] = (char *) class_name; 339 for (i = 0; i <= nargs; i++) 340 argv[2 + i] = (char *) args[i]; 341 342 if (verbose) 343 { 344 char *command = shell_quote_argv (argv); 345 printf ("%s\n", command); 346 free (command); 347 } 348 349 err = executer ("jre", "jre", argv, private_data); 350 351 /* Reset CLASSPATH. */ 352 reset_classpath (old_classpath); 353 354 freea (argv); 355 356 goto done2; 357 } 358 } 359 360#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ 361 /* Win32, Cygwin */ 362 { 363 static bool jview_tested; 364 static bool jview_present; 365 366 if (!jview_tested) 367 { 368 /* Test for presence of jview: "jview -? >nul ; test $? = 1" */ 369 char *argv[3]; 370 int exitstatus; 371 372 argv[0] = "jview"; 373 argv[1] = "-?"; 374 argv[2] = NULL; 375 exitstatus = execute ("jview", "jview", argv, false, false, true, true, 376 true, false); 377 jview_present = (exitstatus == 0 || exitstatus == 1); 378 jview_tested = true; 379 } 380 381 if (jview_present) 382 { 383 char *old_classpath; 384 char **argv = (char **) xmalloca ((2 + nargs + 1) * sizeof (char *)); 385 unsigned int i; 386 387 /* Set CLASSPATH. */ 388 old_classpath = 389 set_classpath (classpaths, classpaths_count, use_minimal_classpath, 390 verbose); 391 392 argv[0] = "jview"; 393 argv[1] = (char *) class_name; 394 for (i = 0; i <= nargs; i++) 395 argv[2 + i] = (char *) args[i]; 396 397 if (verbose) 398 { 399 char *command = shell_quote_argv (argv); 400 printf ("%s\n", command); 401 free (command); 402 } 403 404 err = executer ("jview", "jview", argv, private_data); 405 406 /* Reset CLASSPATH. */ 407 reset_classpath (old_classpath); 408 409 freea (argv); 410 411 goto done2; 412 } 413 } 414#endif 415 416 if (!quiet) 417 error (0, 0, _("Java virtual machine not found, try installing gij or set $JAVA")); 418 err = true; 419 420 done2: 421 if (old_JAVA_HOME != NULL) 422 { 423 xsetenv ("JAVA_HOME", old_JAVA_HOME, 1); 424 free (old_JAVA_HOME); 425 } 426 427 done1: 428 return err; 429} 430