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