exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / javaexec.c
blob081c54eaaa39d2af35e8dc339beab58a4e52797e
1 /* Execute a Java program.
2 Copyright (C) 2001-2003, 2006-2024 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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.
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.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18 #include <config.h>
19 #include <alloca.h>
21 /* Specification. */
22 #include "javaexec.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
28 #include "execute.h"
29 #include "classpath.h"
30 #include "xsetenv.h"
31 #include "sh-quote.h"
32 #include "concat-filename.h"
33 #include "xalloc.h"
34 #include "xmalloca.h"
35 #include <error.h>
36 #include "gettext.h"
38 #define _(str) gettext (str)
41 /* Survey of Java virtual machines.
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
48 Program from A B C T
50 $JAVA unknown N Y n/a true
51 java JDK 1.1.8 Y Y -classpath P java -version 2>/dev/null
52 jre JDK 1.1.8 N Y -classpath P jre 2>/dev/null; test $? = 1
53 java JDK 1.3.0 Y Y -classpath P java -version 2>/dev/null
55 The CLASSPATH is a colon separated list of pathnames. (On Windows: a
56 semicolon separated list of pathnames.)
58 We try the Java virtual machines in the following order:
59 1. getenv ("JAVA"), because the user must be able to override our
60 preferences,
61 2. "java", because it is a standard JVM,
62 3. "jre", comes last because it requires a CLASSPATH environment variable.
64 We unset the JAVA_HOME environment variable, because a wrong setting of
65 this variable can confuse the JDK's javac.
68 bool
69 execute_java_class (const char *class_name,
70 const char * const *classpaths,
71 unsigned int classpaths_count,
72 bool use_minimal_classpath,
73 const char *exe_dir,
74 const char * const *args,
75 bool verbose, bool quiet,
76 execute_fn *executer, void *private_data)
78 bool err = false;
79 unsigned int nargs;
80 char *old_JAVA_HOME;
82 /* Count args. */
84 const char * const *arg;
86 for (nargs = 0, arg = args; *arg != NULL; nargs++, arg++)
90 /* First, try a class compiled to a native code executable. */
91 if (exe_dir != NULL)
93 char *exe_pathname = xconcatenated_filename (exe_dir, class_name, EXEEXT);
94 char *old_classpath;
95 const char **argv =
96 (const char **) xmalloca ((1 + nargs + 1) * sizeof (const char *));
97 unsigned int i;
99 /* Set CLASSPATH. */
100 old_classpath =
101 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
102 verbose);
104 argv[0] = exe_pathname;
105 for (i = 0; i <= nargs; i++)
106 argv[1 + i] = args[i];
108 if (verbose)
110 char *command = shell_quote_argv (argv);
111 printf ("%s\n", command);
112 free (command);
115 err = executer (class_name, exe_pathname, argv, private_data);
117 /* Reset CLASSPATH. */
118 reset_classpath (old_classpath);
120 freea (argv);
122 goto done1;
126 const char *java = getenv ("JAVA");
127 if (java != NULL && java[0] != '\0')
129 /* Because $JAVA may consist of a command and options, we use the
130 shell. Because $JAVA has been set by the user, we leave all
131 all environment variables in place, including JAVA_HOME, and
132 we don't erase the user's CLASSPATH. */
133 char *old_classpath;
134 unsigned int command_length;
135 char *command;
136 const char *argv[4];
137 const char * const *arg;
138 char *p;
140 /* Set CLASSPATH. */
141 old_classpath =
142 set_classpath (classpaths, classpaths_count, false,
143 verbose);
145 command_length = strlen (java);
146 command_length += 1 + shell_quote_length (class_name);
147 for (arg = args; *arg != NULL; arg++)
148 command_length += 1 + shell_quote_length (*arg);
149 command_length += 1;
151 command = (char *) xmalloca (command_length);
152 p = command;
153 /* Don't shell_quote $JAVA, because it may consist of a command
154 and options. */
155 memcpy (p, java, strlen (java));
156 p += strlen (java);
157 *p++ = ' ';
158 p = shell_quote_copy (p, class_name);
159 for (arg = args; *arg != NULL; arg++)
161 *p++ = ' ';
162 p = shell_quote_copy (p, *arg);
164 *p++ = '\0';
165 /* Ensure command_length was correctly calculated. */
166 if (p - command > command_length)
167 abort ();
169 if (verbose)
170 printf ("%s\n", command);
172 argv[0] = BOURNE_SHELL;
173 argv[1] = "-c";
174 argv[2] = command;
175 argv[3] = NULL;
176 err = executer (java, BOURNE_SHELL, argv, private_data);
178 freea (command);
180 /* Reset CLASSPATH. */
181 reset_classpath (old_classpath);
183 goto done1;
187 /* Unset the JAVA_HOME environment variable. */
188 old_JAVA_HOME = getenv ("JAVA_HOME");
189 if (old_JAVA_HOME != NULL)
191 old_JAVA_HOME = xstrdup (old_JAVA_HOME);
192 unsetenv ("JAVA_HOME");
196 static bool java_tested;
197 static bool java_present;
199 if (!java_tested)
201 /* Test for presence of java: "java -version 2> /dev/null" */
202 const char *argv[3];
203 int exitstatus;
205 argv[0] = "java";
206 argv[1] = "-version";
207 argv[2] = NULL;
208 exitstatus = execute ("java", "java", argv, NULL,
209 false, false, true, true,
210 true, false, NULL);
211 java_present = (exitstatus == 0);
212 java_tested = true;
215 if (java_present)
217 char *old_classpath;
218 const char **argv =
219 (const char **) xmalloca ((2 + nargs + 1) * sizeof (const char *));
220 unsigned int i;
222 /* Set CLASSPATH. We don't use the "-classpath ..." option because
223 in JDK 1.1.x its argument should also contain the JDK's classes.zip,
224 but we don't know its location. (In JDK 1.3.0 it would work.) */
225 old_classpath =
226 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
227 verbose);
229 argv[0] = "java";
230 argv[1] = class_name;
231 for (i = 0; i <= nargs; i++)
232 argv[2 + i] = args[i];
234 if (verbose)
236 char *command = shell_quote_argv (argv);
237 printf ("%s\n", command);
238 free (command);
241 err = executer ("java", "java", argv, private_data);
243 /* Reset CLASSPATH. */
244 reset_classpath (old_classpath);
246 freea (argv);
248 goto done2;
253 static bool jre_tested;
254 static bool jre_present;
256 if (!jre_tested)
258 /* Test for presence of jre: "jre 2> /dev/null ; test $? = 1" */
259 const char *argv[2];
260 int exitstatus;
262 argv[0] = "jre";
263 argv[1] = NULL;
264 exitstatus = execute ("jre", "jre", argv, NULL,
265 false, false, true, true,
266 true, false, NULL);
267 jre_present = (exitstatus == 0 || exitstatus == 1);
268 jre_tested = true;
271 if (jre_present)
273 char *old_classpath;
274 const char **argv =
275 (const char **) xmalloca ((2 + nargs + 1) * sizeof (const char *));
276 unsigned int i;
278 /* Set CLASSPATH. We don't use the "-classpath ..." option because
279 in JDK 1.1.x its argument should also contain the JDK's classes.zip,
280 but we don't know its location. */
281 old_classpath =
282 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
283 verbose);
285 argv[0] = "jre";
286 argv[1] = class_name;
287 for (i = 0; i <= nargs; i++)
288 argv[2 + i] = args[i];
290 if (verbose)
292 char *command = shell_quote_argv (argv);
293 printf ("%s\n", command);
294 free (command);
297 err = executer ("jre", "jre", argv, private_data);
299 /* Reset CLASSPATH. */
300 reset_classpath (old_classpath);
302 freea (argv);
304 goto done2;
308 if (!quiet)
309 error (0, 0, _("Java virtual machine not found, try setting $JAVA"));
310 err = true;
312 done2:
313 if (old_JAVA_HOME != NULL)
315 xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
316 free (old_JAVA_HOME);
319 done1:
320 return err;