]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Routines for compiling and executing Java programs.
authorBruno Haible <bruno@clisp.org>
Mon, 17 Sep 2001 12:43:24 +0000 (12:43 +0000)
committerBruno Haible <bruno@clisp.org>
Mon, 17 Sep 2001 12:43:24 +0000 (12:43 +0000)
lib/ChangeLog
lib/Makefile.am
lib/classpath.c [new file with mode: 0644]
lib/javacomp.c [new file with mode: 0644]
lib/javacomp.h [new file with mode: 0644]
lib/javaexec.c [new file with mode: 0644]
lib/javaexec.h [new file with mode: 0644]

index dd4ad0fdf7c13336a74a8f2398cfd33131289d77..1560eca4efeecc77619598b98b8b4bc59276f847 100644 (file)
@@ -1,3 +1,14 @@
+2001-09-06  Bruno Haible  <haible@clisp.cons.org>
+
+       * javacomp.h: New file.
+       * javacomp.c: New file.
+       * javaexec.h: New file.
+       * javaexec.c: New file.
+       * classpath.c: New file.
+       * Makefile.am (EXTRA_DIST): Add classpath.c.
+       (libnlsut_a_SOURCES): Add javacomp.c, javaexec.c.
+       (noinst_HEADERS): Add javacomp.h, javaexec.h.
+
 2001-09-06  Bruno Haible  <haible@clisp.cons.org>
 
        * sh-quote.h: New file.
index cec62be3b77e5c8fbfd928ee5fccbdcb37b0a879..7b8dec60cf910666be7b760596b73ec34a5d1418 100644 (file)
@@ -21,25 +21,26 @@ AUTOMAKE_OPTIONS = 1.2 gnits
 
 noinst_LIBRARIES = libnlsut.a
 
-EXTRA_DIST = alloca.c config.charset error.c getline.c memset.c memmove.c \
-mkdtemp.c ref-add.sin ref-del.sin setenv.c stpcpy.c stpncpy.c strcasecmp.c \
-strcspn.c strncasecmp.c strpbrk.c strstr.c strtol.c strtoul.c vasprintf.c \
+EXTRA_DIST = alloca.c classpath.c config.charset error.c getline.c memset.c \
+memmove.c mkdtemp.c ref-add.sin ref-del.sin setenv.c stpcpy.c stpncpy.c \
+strcasecmp.c strcspn.c strncasecmp.c strpbrk.c strstr.c strtol.c strtoul.c \
+vasprintf.c \
 stdbool.h.in \
 gen-lbrkprop.c 3level.h
 
 libnlsut_a_SOURCES = basename.c c-ctype.c concatpath.c execute.c findprog.c \
-fstrcmp.c full-write.c gcd.c getopt.c getopt1.c hash.c linebreak.c \
-localcharset.c mbswidth.c obstack.c pipe-bidi.c pipe-in.c pipe-out.c \
-progname.c safe-read.c sh-quote.c tmpdir.c wait-process.c xerror.c xgetcwd.c \
-xmalloc.c xstrdup.c
+fstrcmp.c full-write.c gcd.c getopt.c getopt1.c hash.c javacomp.c javaexec.c \
+linebreak.c localcharset.c mbswidth.c obstack.c pipe-bidi.c pipe-in.c \
+pipe-out.c progname.c safe-read.c sh-quote.c tmpdir.c wait-process.c xerror.c \
+xgetcwd.c xmalloc.c xstrdup.c
 
 libnlsut_a_LIBADD = @ALLOCA@ @LIBOBJS@
 
 noinst_HEADERS = c-ctype.h error.h execute.h findprog.h fstrcmp.h \
-full-write.h gcd.h getline.h getopt.h hash.h lbrkprop.h linebreak.h mbswidth.h \
-mkdtemp.h obstack.h pathmax.h pipe.h progname.h safe-read.h setenv.h \
-sh-quote.h strpbrk.h system.h tmpdir.h utf8-ucs4.h utf16-ucs4.h wait-process.h \
-xerror.h
+full-write.h gcd.h getline.h getopt.h hash.h javacomp.h javaexec.h lbrkprop.h \
+linebreak.h mbswidth.h mkdtemp.h obstack.h pathmax.h pipe.h progname.h \
+safe-read.h setenv.h sh-quote.h strpbrk.h system.h tmpdir.h utf8-ucs4.h \
+utf16-ucs4.h wait-process.h xerror.h
 
 DEFS = -DLIBDIR=\"$(libdir)\" @DEFS@
 INCLUDES = -I. -I$(srcdir) -I.. -I../intl
diff --git a/lib/classpath.c b/lib/classpath.c
new file mode 100644 (file)
index 0000000..05fb45a
--- /dev/null
@@ -0,0 +1,126 @@
+/* Java CLASSPATH handling.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+
+/* Separator in PATH like lists of pathnames.  */
+#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
+  /* Win32, OS/2, DOS */
+# define PATH_SEPARATOR ';'
+#else
+  /* Unix */
+# define PATH_SEPARATOR ':' 
+#endif
+
+
+/* Prototypes for local functions.  Needed to ensure compiler checking of
+   function argument counts despite of K&R C function definition syntax.  */
+static char * new_classpath PARAMS ((const char * const *classpaths,
+                                    unsigned int classpaths_count,
+                                    bool use_minimal_classpath));
+static char * set_classpath PARAMS ((const char * const *classpaths,
+                                    unsigned int classpaths_count,
+                                    bool use_minimal_classpath,
+                                    bool verbose));
+static void reset_classpath PARAMS ((char *old_classpath));
+
+
+/* Return the new CLASSPATH value.  The given classpaths are prepended to
+   the current CLASSPATH value.   If use_minimal_classpath, the current
+   CLASSPATH is ignored.  */
+static char *
+new_classpath (classpaths, classpaths_count, use_minimal_classpath)
+     const char * const *classpaths;
+     unsigned int classpaths_count;
+     bool use_minimal_classpath;
+{
+  const char *old_classpath;
+  unsigned int length;
+  unsigned int i;
+  char *result;
+  char *p;
+
+  old_classpath = (use_minimal_classpath ? NULL : getenv ("CLASSPATH"));
+  if (old_classpath == NULL)
+    old_classpath = "";
+
+  length = 0;
+  for (i = 0; i < classpaths_count; i++)
+    length += strlen (classpaths[i]) + 1;
+  length += strlen (old_classpath);
+  if (classpaths_count > 0 && old_classpath[0] == '\0')
+    length--;
+
+  result = (char *) xmalloc (length + 1);
+  p = result;
+  for (i = 0; i < classpaths_count; i++)
+    {
+      memcpy (p, classpaths[i], strlen (classpaths[i]));
+      p += strlen (classpaths[i]);
+      *p++ = PATH_SEPARATOR;
+    }
+  if (old_classpath[0] != '\0')
+    {
+      memcpy (p, old_classpath, strlen (old_classpath));
+      p += strlen (old_classpath);
+    }
+  else
+    {
+      if (classpaths_count > 0)
+       p--;
+    }
+  *p = '\0';
+
+  return result;
+}
+
+/* Set CLASSPATH and returns a safe copy of its old value.  */
+static char *
+set_classpath (classpaths, classpaths_count, use_minimal_classpath, verbose)
+     const char * const *classpaths;
+     unsigned int classpaths_count;
+     bool use_minimal_classpath;
+     bool verbose;
+{
+  const char *old_CLASSPATH = getenv ("CLASSPATH");
+  char *result = (old_CLASSPATH != NULL ? xstrdup (old_CLASSPATH) : NULL);
+  char *new_CLASSPATH =
+    new_classpath (classpaths, classpaths_count, use_minimal_classpath);
+
+  if (verbose)
+    printf ("CLASSPATH=%s ", new_CLASSPATH);
+
+  setenv ("CLASSPATH", new_CLASSPATH, 1);
+
+  free (new_CLASSPATH);
+
+  return result;
+}
+
+/* Restore CLASSPATH to its previous value.  */
+static void
+reset_classpath (old_classpath)
+     char *old_classpath;
+{
+  if (old_classpath != NULL)
+    {
+      setenv ("CLASSPATH", old_classpath, 1);
+      free (old_classpath);
+    }
+  else
+    unsetenv ("CLASSPATH");
+}
diff --git a/lib/javacomp.c b/lib/javacomp.c
new file mode 100644 (file)
index 0000000..6ed8714
--- /dev/null
@@ -0,0 +1,420 @@
+/* Compile a Java program.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include "javacomp.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "execute.h"
+#include "setenv.h"
+#include "sh-quote.h"
+#include "system.h"
+#include "error.h"
+#include "libgettext.h"
+
+#define _(str) gettext (str)
+
+
+/* CLASSPATH handling subroutines.  */
+#include "classpath.c"
+
+/* Survey of Java compilers.
+
+   A = does it work without CLASSPATH being set
+   C = option to set CLASSPATH, other than setting it in the environment
+   O = option for optimizing
+   g = option for debugging
+   T = test for presence
+
+   Program  from        A  C               O  g  T
+
+   $JAVAC   unknown     N  n/a            -O -g  true
+   gcj -C   GCC 3.0     Y  --classpath=P  -O -g  gcj --version >/dev/null
+   javac    JDK 1.1.8   Y  -classpath P   -O -g  javac 2>/dev/null; test $? = 1
+   javac    JDK 1.3.0   Y  -classpath P   -O -g  javac 2>/dev/null; test $? = 1
+   jikes    Jikes 1.14  N  -classpath P   -O -g  jikes 2>/dev/null; test $? = 1
+
+   All compilers support the option "-d DIRECTORY" for the base directory
+   of the classes to be written.
+
+   The CLASSPATH is a colon separated list of pathnames. (On Windows: a
+   semicolon separated list of pathnames.)
+
+   We try the Java compilers in the following order:
+     1. getenv ("JAVAC"), because the user must be able to override our
+       preferences,
+     2. "gcj -C", because it is a completely free compiler,
+     3. "javac", because it is a standard compiler,
+     4. "jikes", comes last because it has some deviating interpretation
+       of the Java Language Specification and because it requires a
+       CLASSPATH environment variable.
+
+   We unset the JAVA_HOME environment variable, because a wrong setting of
+   this variable can confuse the JDK's javac.
+ */
+
+bool
+compile_java_class (java_sources, java_sources_count,
+                   classpaths, classpaths_count,
+                   directory, optimize, debug, use_minimal_classpath, verbose)
+     const char * const *java_sources;
+     unsigned int java_sources_count;
+     const char * const *classpaths;
+     unsigned int classpaths_count;
+     const char *directory;
+     bool optimize;
+     bool debug;
+     bool use_minimal_classpath;
+     bool verbose;
+{
+  bool err = false;
+  char *old_JAVA_HOME;
+
+  {
+    const char *javac = getenv ("JAVAC");
+    if (javac != NULL && javac[0] != '\0')
+      {
+       /* Because $JAVAC may consist of a command and options, we use the
+          shell.  Because $JAVAC has been set by the user, we leave all
+          all environment variables in place, including JAVA_HOME, and
+          we don't erase the user's CLASSPATH.  */
+       char *old_classpath;
+       unsigned int command_length;
+       char *command;
+       char *argv[4];
+       int exitstatus;
+       unsigned int i;
+       char *p;
+
+       /* Set CLASSPATH.  */
+       old_classpath =
+         set_classpath (classpaths, classpaths_count, false,
+                        verbose);
+
+       command_length = strlen (javac);
+       if (optimize)
+         command_length += 3;
+       if (debug)
+         command_length += 3;
+       if (directory != NULL)
+         command_length += 4 + shell_quote_length (directory);
+       for (i = 0; i < java_sources_count; i++)
+         command_length += 1 + shell_quote_length (java_sources[i]);
+       command_length += 1;
+
+       command = (char *) alloca (command_length);
+       p = command;
+       /* Don't shell_quote $JAVAC, because it may consist of a command
+          and options.  */
+       memcpy (p, javac, strlen (javac));
+       p += strlen (javac);
+       if (optimize)
+         {
+           memcpy (p, " -O", 3);
+           p += 3;
+         }
+       if (debug)
+         {
+           memcpy (p, " -g", 3);
+           p += 3;
+         }
+       if (directory != NULL)
+         {
+           memcpy (p, " -d ", 4);
+           p += 4;
+           p = shell_quote_copy (p, directory);
+         }
+       for (i = 0; i < java_sources_count; i++)
+         {
+           *p++ = ' ';
+           p = shell_quote_copy (p, java_sources[i]);
+         }
+       *p++ = '\0';
+       /* Ensure command_length was correctly calculated.  */
+       if (p - command > command_length)
+         abort ();
+
+       if (verbose)
+         printf ("%s\n", command);
+
+       argv[0] = "/bin/sh";
+       argv[1] = "-c";
+       argv[2] = command;
+       argv[3] = NULL;
+       exitstatus = execute (javac, "/bin/sh", argv, false, false, false);
+       err = (exitstatus != 0);
+
+       /* Reset CLASSPATH.  */
+       reset_classpath (old_classpath);
+
+       goto done1;
+      }
+  }
+
+  /* Unset the JAVA_HOME environment variable.  */
+  old_JAVA_HOME = getenv ("JAVA_HOME");
+  if (old_JAVA_HOME != NULL)
+    {
+      old_JAVA_HOME = xstrdup (old_JAVA_HOME);
+      unsetenv ("JAVA_HOME");
+    }
+
+  {
+    static bool gcj_tested;
+    static bool gcj_present;
+
+    if (!gcj_tested)
+      {
+       /* Test for presence of gcj: "gcj --version > /dev/null"  */
+       char *argv[3];
+       int exitstatus;
+
+       argv[0] = "gcj";
+       argv[1] = "--version";
+       argv[2] = NULL;
+       exitstatus = execute ("gcj", "gcj", argv, false, true, true);
+       gcj_present = (exitstatus == 0);
+       gcj_tested = true;
+      }
+
+    if (gcj_present)
+      {
+       char *old_classpath;
+       unsigned int argc;
+       char **argv;
+       char **argp;
+       int exitstatus;
+       unsigned int i;
+
+       /* Set CLASSPATH.  We could also use the --CLASSPATH=... option
+          of gcj.  Note that --classpath=... option is different: its
+          argument should also contain gcj's libgcj.jar, but we don't
+          know its location.  */
+       old_classpath =
+         set_classpath (classpaths, classpaths_count, use_minimal_classpath,
+                        verbose);
+
+       argc =
+         2 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
+         + java_sources_count;
+       argv = (char **) alloca ((argc + 1) * sizeof (char *));
+
+       argp = argv;
+       *argp++ = "gcj";
+       *argp++ = "-C";
+       if (optimize)
+         *argp++ = "-O";
+       if (debug)
+         *argp++ = "-g";
+       if (directory != NULL)
+         {
+           *argp++ = "-d";
+           *argp++ = (char *) directory;
+         }
+       for (i = 0; i < java_sources_count; i++)
+         *argp++ = (char *) java_sources[i];
+       *argp = NULL;
+       /* Ensure argv length was correctly calculated.  */
+       if (argp - argv != argc)
+         abort ();
+
+       if (verbose)
+         {
+           char *command = shell_quote_argv (argv);
+           printf ("%s\n", command);
+           free (command);
+         }
+
+       exitstatus = execute ("gcj", "gcj", argv, false, false, false);
+       err = (exitstatus != 0);
+
+       /* Reset CLASSPATH.  */
+       reset_classpath (old_classpath);
+
+       goto done2;
+      }
+  }
+
+  {
+    static bool javac_tested;
+    static bool javac_present;
+
+    if (!javac_tested)
+      {
+       /* Test for presence of javac: "javac 2> /dev/null ; test $? = 1"  */
+       char *argv[2];
+       int exitstatus;
+
+       argv[0] = "javac";
+       argv[1] = NULL;
+       exitstatus = execute ("javac", "javac", argv, false, true, true);
+       javac_present = (exitstatus == 0 || exitstatus == 1);
+       javac_tested = true;
+      }
+
+    if (javac_present)
+      {
+       char *old_classpath;
+       unsigned int argc;
+       char **argv;
+       char **argp;
+       int exitstatus;
+       unsigned int i;
+
+       /* Set CLASSPATH.  We don't use the "-classpath ..." option because
+          in JDK 1.1.x its argument should also contain the JDK's classes.zip,
+          but we don't know its location.  (In JDK 1.3.0 it would work.)  */
+       old_classpath =
+         set_classpath (classpaths, classpaths_count, use_minimal_classpath,
+                        verbose);
+
+       argc =
+         1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
+         + java_sources_count;
+       argv = (char **) alloca ((argc + 1) * sizeof (char *));
+
+       argp = argv;
+       *argp++ = "javac";
+       if (optimize)
+         *argp++ = "-O";
+       if (debug)
+         *argp++ = "-g";
+       if (directory != NULL)
+         {
+           *argp++ = "-d";
+           *argp++ = (char *) directory;
+         }
+       for (i = 0; i < java_sources_count; i++)
+         *argp++ = (char *) java_sources[i];
+       *argp = NULL;
+       /* Ensure argv length was correctly calculated.  */
+       if (argp - argv != argc)
+         abort ();
+
+       if (verbose)
+         {
+           char *command = shell_quote_argv (argv);
+           printf ("%s\n", command);
+           free (command);
+         }
+
+       exitstatus = execute ("javac", "javac", argv, false, false, false);
+       err = (exitstatus != 0);
+
+       /* Reset CLASSPATH.  */
+       reset_classpath (old_classpath);
+
+       goto done2;
+      }
+  }
+
+  {
+    static bool jikes_tested;
+    static bool jikes_present;
+
+    if (!jikes_tested)
+      {
+       /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1"  */
+       char *argv[2];
+       int exitstatus;
+
+       argv[0] = "jikes";
+       argv[1] = NULL;
+       exitstatus = execute ("jikes", "jikes", argv, false, true, true);
+       jikes_present = (exitstatus == 0 || exitstatus == 1);
+       jikes_tested = true;
+      }
+
+    if (jikes_present)
+      {
+       char *old_classpath;
+       unsigned int argc;
+       char **argv;
+       char **argp;
+       int exitstatus;
+       unsigned int i;
+
+       /* Set CLASSPATH.  We could also use the "-classpath ..." option.
+          Since jikes doesn't come with its own standard library, it
+          needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
+          To increase the chance of success, we reuse the current CLASSPATH
+          if the user has set it.  */
+       old_classpath =
+         set_classpath (classpaths, classpaths_count, false,
+                        verbose);
+
+       argc =
+         1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
+         + java_sources_count;
+       argv = (char **) alloca ((argc + 1) * sizeof (char *));
+
+       argp = argv;
+       *argp++ = "jikes";
+       if (optimize)
+         *argp++ = "-O";
+       if (debug)
+         *argp++ = "-g";
+       if (directory != NULL)
+         {
+           *argp++ = "-d";
+           *argp++ = (char *) directory;
+         }
+       for (i = 0; i < java_sources_count; i++)
+         *argp++ = (char *) java_sources[i];
+       *argp = NULL;
+       /* Ensure argv length was correctly calculated.  */
+       if (argp - argv != argc)
+         abort ();
+
+       if (verbose)
+         {
+           char *command = shell_quote_argv (argv);
+           printf ("%s\n", command);
+           free (command);
+         }
+
+       exitstatus = execute ("jikes", "jikes", argv, false, false, false);
+       err = (exitstatus != 0);
+
+       /* Reset CLASSPATH.  */
+       reset_classpath (old_classpath);
+
+       goto done2;
+      }
+  }
+
+  error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
+  err = true;
+
+ done2:
+  if (old_JAVA_HOME != NULL)
+    {
+      setenv ("JAVA_HOME", old_JAVA_HOME, 1);
+      free (old_JAVA_HOME);
+    }
+
+ done1:
+  return err;
+}
diff --git a/lib/javacomp.h b/lib/javacomp.h
new file mode 100644 (file)
index 0000000..83c435c
--- /dev/null
@@ -0,0 +1,45 @@
+/* Compile a Java program.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _JAVACOMP_H
+#define _JAVACOMP_H
+
+#include <stdbool.h>
+
+/* Compile a Java source file to bytecode.
+   java_sources is an array of source file names.
+   classpaths is a list of pathnames to be prepended to the CLASSPATH.
+   directory is the target directory. The .class file for class X.Y.Z is
+   written at directory/X/Y/Z.class. If directory is NULL, the .class
+   file is written in the source's directory.
+   use_minimal_classpath = true means to ignore the user's CLASSPATH and
+   use a minimal one. This is likely to reduce possible problems if the
+   user's CLASSPATH contains garbage or a classes.zip file of the wrong
+   Java version.
+   If verbose, the command to be executed will be printed.
+   Return false if OK, true on error.  */
+extern bool compile_java_class PARAMS ((const char * const *java_sources,
+                                       unsigned int java_sources_count,
+                                       const char * const *classpaths,
+                                       unsigned int classpaths_count,
+                                       const char *directory,
+                                       bool optimize, bool debug,
+                                       bool use_minimal_classpath,
+                                       bool verbose));
+
+#endif /* _JAVACOMP_H */
diff --git a/lib/javaexec.c b/lib/javaexec.c
new file mode 100644 (file)
index 0000000..3053b37
--- /dev/null
@@ -0,0 +1,387 @@
+/* Execute a Java program.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include "javaexec.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "execute.h"
+#include "setenv.h"
+#include "sh-quote.h"
+#include "system.h"
+#include "error.h"
+#include "libgettext.h"
+
+#define _(str) gettext (str)
+
+
+/* CLASSPATH handling subroutines.  */
+#include "classpath.c"
+
+/* Survey of Java virtual machines.
+
+   A = does it work without CLASSPATH being set
+   B = does it work with CLASSPATH being set to empty
+   C = option to set CLASSPATH, other than setting it in the environment
+   T = test for presence
+
+   Program    from         A B  C              T
+
+   $JAVA      unknown      N Y  n/a            true
+   gij        GCC 3.0      Y Y  n/a            gij --version >/dev/null
+   java       JDK 1.1.8    Y Y  -classpath P   java -version 2>/dev/null
+   jre        JDK 1.1.8    N Y  -classpath P   jre 2>/dev/null; test $? = 1
+   java       JDK 1.3.0    Y Y  -classpath P   java -version 2>/dev/null
+   jview      MS IE        Y Y  -cp P          jview -? >nul; %errorlevel% = 1
+
+   The CLASSPATH is a colon separated list of pathnames. (On Windows: a
+   semicolon separated list of pathnames.)
+
+   We try the Java virtual machines in the following order:
+     1. getenv ("JAVA"), because the user must be able to override our
+       preferences,
+     2. "gij", because it is a completely free JVM,
+     3. "java", because it is a standard JVM,
+     4. "jre", comes last because it requires a CLASSPATH environment variable,
+     5. "jview", on Windows only, because it is frequently installed.
+
+   We unset the JAVA_HOME environment variable, because a wrong setting of
+   this variable can confuse the JDK's javac.
+ */
+
+bool
+execute_java_class (class_name,
+                   classpaths, classpaths_count, use_minimal_classpath,
+                   args,
+                   verbose,
+                   executer, private_data)
+     const char *class_name;
+     const char * const *classpaths;
+     unsigned int classpaths_count;
+     bool use_minimal_classpath;
+     const char * const *args;
+     bool verbose;
+     execute_fn *executer;
+     void *private_data;
+{
+  bool err = false;
+  unsigned int nargs;
+  char *old_JAVA_HOME;
+
+  {
+    const char *java = getenv ("JAVA");
+    if (java != NULL && java[0] != '\0')
+      {
+       /* Because $JAVA may consist of a command and options, we use the
+          shell.  Because $JAVA has been set by the user, we leave all
+          all environment variables in place, including JAVA_HOME, and
+          we don't erase the user's CLASSPATH.  */
+       char *old_classpath;
+       unsigned int command_length;
+       char *command;
+       char *argv[4];
+       const char * const *arg;
+       char *p;
+
+       /* Set CLASSPATH.  */
+       old_classpath =
+         set_classpath (classpaths, classpaths_count, false,
+                        verbose);
+
+       command_length = strlen (java);
+       command_length += 1 + shell_quote_length (class_name);
+       for (arg = args; *arg != NULL; arg++)
+         command_length += 1 + shell_quote_length (*arg);
+       command_length += 1;
+
+       command = (char *) alloca (command_length);
+       p = command;
+       /* Don't shell_quote $JAVA, because it may consist of a command
+          and options.  */
+       memcpy (p, java, strlen (java));
+       p += strlen (java);
+       *p++ = ' ';
+       p = shell_quote_copy (p, class_name);
+       for (arg = args; *arg != NULL; arg++)
+         {
+           *p++ = ' ';
+           p = shell_quote_copy (p, *arg);
+         }
+       *p++ = '\0';
+       /* Ensure command_length was correctly calculated.  */
+       if (p - command > command_length)
+         abort ();
+
+       if (verbose)
+         printf ("%s\n", command);
+
+       argv[0] = "/bin/sh";
+       argv[1] = "-c";
+       argv[2] = command;
+       argv[3] = NULL;
+       err = executer (java, "/bin/sh", argv, private_data);
+
+       /* Reset CLASSPATH.  */
+       reset_classpath (old_classpath);
+
+       goto done1;
+      }
+  }
+
+  /* Count args.  */
+  {
+    const char * const *arg;
+
+    for (nargs = 0, arg = args; *arg != NULL; nargs++, arg++)
+     ;
+  }
+
+  /* Unset the JAVA_HOME environment variable.  */
+  old_JAVA_HOME = getenv ("JAVA_HOME");
+  if (old_JAVA_HOME != NULL)
+    {
+      old_JAVA_HOME = xstrdup (old_JAVA_HOME);
+      unsetenv ("JAVA_HOME");
+    }
+
+  {
+    static bool gij_tested;
+    static bool gij_present;
+
+    if (!gij_tested)
+      {
+       /* Test for presence of gij: "gij --version > /dev/null"  */
+       char *argv[3];
+       int exitstatus;
+
+       argv[0] = "gij";
+       argv[1] = "--version";
+       argv[2] = NULL;
+       exitstatus = execute ("gij", "gij", argv, false, true, true);
+       gij_present = (exitstatus == 0);
+       gij_tested = true;
+      }
+
+    if (gij_present)
+      {
+       char *old_classpath;
+       char **argv = (char **) alloca ((2 + nargs + 1) * sizeof (char *));
+       unsigned int i;
+
+       /* Set CLASSPATH.  */
+       old_classpath =
+         set_classpath (classpaths, classpaths_count, use_minimal_classpath,
+                        verbose);
+
+       argv[0] = "gij";
+       argv[1] = (char *) class_name;
+       for (i = 0; i <= nargs; i++)
+         argv[2 + i] = (char *) args[i];
+
+       if (verbose)
+         {
+           char *command = shell_quote_argv (argv);
+           printf ("%s\n", command);
+           free (command);
+         }
+
+       err = executer ("gij", "gij", argv, private_data);
+
+       /* Reset CLASSPATH.  */
+       reset_classpath (old_classpath);
+
+       goto done2;
+      }
+  }
+
+  {
+    static bool java_tested;
+    static bool java_present;
+
+    if (!java_tested)
+      {
+       /* Test for presence of java: "java -version 2> /dev/null"  */
+       char *argv[3];
+       int exitstatus;
+
+       argv[0] = "java";
+       argv[1] = "-version";
+       argv[2] = NULL;
+       exitstatus = execute ("java", "java", argv, false, true, true);
+       java_present = (exitstatus == 0);
+       java_tested = true;
+      }
+
+    if (java_present)
+      {
+       char *old_classpath;
+       char **argv = (char **) alloca ((2 + nargs + 1) * sizeof (char *));
+       unsigned int i;
+
+       /* Set CLASSPATH.  We don't use the "-classpath ..." option because
+          in JDK 1.1.x its argument should also contain the JDK's classes.zip,
+          but we don't know its location.  (In JDK 1.3.0 it would work.)  */
+       old_classpath =
+         set_classpath (classpaths, classpaths_count, use_minimal_classpath,
+                        verbose);
+
+       argv[0] = "java";
+       argv[1] = (char *) class_name;
+       for (i = 0; i <= nargs; i++)
+         argv[2 + i] = (char *) args[i];
+
+       if (verbose)
+         {
+           char *command = shell_quote_argv (argv);
+           printf ("%s\n", command);
+           free (command);
+         }
+
+       err = executer ("java", "java", argv, private_data);
+
+       /* Reset CLASSPATH.  */
+       reset_classpath (old_classpath);
+
+       goto done2;
+      }
+  }
+
+  {
+    static bool jre_tested;
+    static bool jre_present;
+
+    if (!jre_tested)
+      {
+       /* Test for presence of jre: "jre 2> /dev/null ; test $? = 1"  */
+       char *argv[2];
+       int exitstatus;
+
+       argv[0] = "jre";
+       argv[1] = NULL;
+       exitstatus = execute ("jre", "jre", argv, false, true, true);
+       jre_present = (exitstatus == 0 || exitstatus == 1);
+       jre_tested = true;
+      }
+
+    if (jre_present)
+      {
+       char *old_classpath;
+       char **argv = (char **) alloca ((2 + nargs + 1) * sizeof (char *));
+       unsigned int i;
+
+       /* Set CLASSPATH.  We don't use the "-classpath ..." option because
+          in JDK 1.1.x its argument should also contain the JDK's classes.zip,
+          but we don't know its location.  */
+       old_classpath =
+         set_classpath (classpaths, classpaths_count, use_minimal_classpath,
+                        verbose);
+
+       argv[0] = "jre";
+       argv[1] = (char *) class_name;
+       for (i = 0; i <= nargs; i++)
+         argv[2 + i] = (char *) args[i];
+
+       if (verbose)
+         {
+           char *command = shell_quote_argv (argv);
+           printf ("%s\n", command);
+           free (command);
+         }
+
+       err = executer ("jre", "jre", argv, private_data);
+
+       /* Reset CLASSPATH.  */
+       reset_classpath (old_classpath);
+
+       goto done2;
+      }
+  }
+
+#if defined _WIN32 || defined __WIN32__
+  /* Win32 */
+  {
+    static bool jview_tested;
+    static bool jview_present;
+
+    if (!jview_tested)
+      {
+       /* Test for presence of jview: "jview -? >nul ; test $? = 1"  */
+       char *argv[3];
+       int exitstatus;
+
+       argv[0] = "jview";
+       argv[1] = "-?";
+       argv[2] = NULL;
+       exitstatus = execute ("jview", "jview", argv, false, true, true);
+       jview_present = (exitstatus == 0 || exitstatus == 1);
+       jview_tested = true;
+      }
+
+    if (jview_present)
+      {
+       char *old_classpath;
+       char **argv = (char **) alloca ((2 + nargs + 1) * sizeof (char *));
+       unsigned int i;
+
+       /* Set CLASSPATH.  */
+       old_classpath =
+         set_classpath (classpaths, classpaths_count, use_minimal_classpath,
+                        verbose);
+
+       argv[0] = "jview";
+       argv[1] = (char *) class_name;
+       for (i = 0; i <= nargs; i++)
+         argv[2 + i] = (char *) args[i];
+
+       if (verbose)
+         {
+           char *command = shell_quote_argv (argv);
+           printf ("%s\n", command);
+           free (command);
+         }
+
+       err = executer ("jview", "jview", argv, private_data);
+
+       /* Reset CLASSPATH.  */
+       reset_classpath (old_classpath);
+
+       goto done2;
+      }
+  }
+#endif
+
+  error (0, 0, _("Java virtual machine not found, try installing gij or set $JAVA"));
+  err = true;
+
+ done2:
+  if (old_JAVA_HOME != NULL)
+    {
+      setenv ("JAVA_HOME", old_JAVA_HOME, 1);
+      free (old_JAVA_HOME);
+    }
+
+ done1:
+  return err;
+}
diff --git a/lib/javaexec.h b/lib/javaexec.h
new file mode 100644 (file)
index 0000000..71561bb
--- /dev/null
@@ -0,0 +1,49 @@
+/* Execute a Java program.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _JAVAEXEC_H
+#define _JAVAEXEC_H
+
+#include <stdbool.h>
+
+typedef bool execute_fn PARAMS ((const char *progname,
+                                const char *prog_path, char **prog_argv,
+                                void *private_data));
+
+/* Execute a Java class.
+   class_name is the Java class name to be executed.
+   classpaths is a list of pathnames to be prepended to the CLASSPATH.
+   use_minimal_classpath = true means to ignore the user's CLASSPATH and
+   use a minimal one. This is likely to reduce possible problems if the
+   user's CLASSPATH contains garbage or a classes.zip file of the wrong
+   Java version.
+   args is a NULL terminated list of arguments to be passed to the program.
+   If verbose, the command to be executed will be printed.
+   Then the command is passed to the execute function together with the
+   private_data argument.  This function returns false if OK, true on error.
+   Return false if OK, true on error.  */
+extern bool execute_java_class PARAMS ((const char *class_name,
+                                       const char * const *classpaths,
+                                       unsigned int classpaths_count,
+                                       bool use_minimal_classpath,
+                                       const char * const *args,
+                                       bool verbose,
+                                       execute_fn *executer,
+                                       void *private_data));
+
+#endif /* _JAVAEXEC_H */