maint: update copyright
[gnulib.git] / lib / javaexec.c
1 /* Execute a Java program.
2    Copyright (C) 2001-2003, 2006-2014 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 "concat-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
72 bool
73 execute_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 = xconcatenated_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, NULL);
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, NULL);
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, NULL);
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   /* Native Windows, 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, NULL);
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 }