OSDN Git Service

2000-07-21 Alexandre Petit-Bianco <apbianco@cygnus.com>
[pf3gnuchains/gcc-fork.git] / gcc / java / gjavah.c
index 5ec6a88..b8a37f7 100644 (file)
@@ -1,7 +1,7 @@
 /* Program to write C++-suitable header files from a Java(TM) .class
    file.  This is similar to SUN's javah.
 
-Copyright (C) 1996, 1998, 1999 Free Software Foundation, Inc.
+Copyright (C) 1996, 1998, 1999, 2000 Free Software Foundation, Inc.
 
 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
@@ -26,9 +26,17 @@ The Free Software Foundation is independent of Sun Microsystems, Inc.  */
 
 #include "config.h"
 #include "system.h"
+#include <math.h>
+
 #include "jcf.h"
+#include "tree.h"
+#include "javaop.h"
+#include "java-tree.h"
 #include "java-opcodes.h"
-#include <math.h>
+
+#include <getopt.h>
+
+\f
 
 /* The output file.  */
 FILE *out = NULL;
@@ -36,6 +44,9 @@ FILE *out = NULL;
 /* Nonzero on failure.  */
 static int found_error = 0;
 
+/* Nonzero if we're generating JNI output.  */
+static int flag_jni = 0;
+
 /* Directory to place resulting files in. Set by -d option. */
 const char *output_directory = "";
 
@@ -72,37 +83,70 @@ int verbose = 0;
 int stubs = 0;
 
 struct JCF *current_jcf;
-struct JCF *main_jcf;
 
 /* This holds access information for the last field we examined.  They
    let us generate "private:", "public:", and "protected:" properly.
    If 0 then we haven't previously examined any field.  */
 static JCF_u2 last_access;
 
-#define ACC_VISIBILITY (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED)
-
 /* Pass this macro the flags for a class and for a method.  It will
    return true if the method should be considered `final'.  */
 #define METHOD_IS_FINAL(Class, Method) \
    (((Class) & ACC_FINAL) || ((Method) & (ACC_FINAL | ACC_PRIVATE)))
 
+/* Pass this macro the flags for a method.  It will return true if the
+   method is native.  */
+#define METHOD_IS_NATIVE(Method) \
+   ((Method) & ACC_NATIVE)
+
 /* We keep a linked list of all method names we have seen.  This lets
    us determine if a method name and a field name are in conflict.  */
 struct method_name
 {
   unsigned char *name;
   int length;
+  unsigned char *signature;
+  int sig_length;
   struct method_name *next;
 };
 
 /* List of method names we've seen.  */
 static struct method_name *method_name_list;
 
-static void print_field_info PROTO ((FILE *, JCF*, int, int, JCF_u2));
-static void print_method_info PROTO ((FILE *, JCF*, int, int, JCF_u2));
-static void print_c_decl PROTO ((FILE*, JCF*, int, int, JCF_u2, int, const char *));
-static void decompile_method PROTO ((FILE *, JCF *, int));
-static void add_class_decl PROTO ((FILE *, JCF *, JCF_u2));
+static void print_field_info PARAMS ((FILE*, JCF*, int, int, JCF_u2));
+static void print_mangled_classname PARAMS ((FILE*, JCF*, const char*, int));
+static int  print_cxx_classname PARAMS ((FILE*, const char*, JCF*, int));
+static void print_method_info PARAMS ((FILE*, JCF*, int, int, JCF_u2));
+static void print_c_decl PARAMS ((FILE*, JCF*, int, int, int, const char *,
+                                 int));
+static void print_stub_or_jni PARAMS ((FILE*, JCF*, int, int, int,
+                                      const char *, int));
+static void print_full_cxx_name PARAMS ((FILE*, JCF*, int, int, int,
+                                        const char *, int));
+static void decompile_method PARAMS ((FILE*, JCF*, int));
+static void add_class_decl PARAMS ((FILE*, JCF*, JCF_u2));
+
+static int java_float_finite PARAMS ((jfloat));
+static int java_double_finite PARAMS ((jdouble));
+static void print_name PARAMS ((FILE *, JCF *, int));
+static void print_base_classname PARAMS ((FILE *, JCF *, int));
+static int utf8_cmp PARAMS ((const unsigned char *, int, const char *));
+static char *cxx_keyword_subst PARAMS ((const unsigned char *, int));
+static void generate_access PARAMS ((FILE *, JCF_u2));
+static int name_is_method_p PARAMS ((const unsigned char *, int));
+static char *get_field_name PARAMS ((JCF *, int, JCF_u2));
+static void print_field_name PARAMS ((FILE *, JCF *, int, JCF_u2));
+static const unsigned char *super_class_name PARAMS ((JCF *, int *));
+static void print_include PARAMS ((FILE *, const unsigned char *, int));
+static const unsigned char *decode_signature_piece
+  PARAMS ((FILE *, const unsigned char *, const unsigned char *, int *));
+static void print_class_decls PARAMS ((FILE *, JCF *, int));
+static void usage PARAMS ((void)) ATTRIBUTE_NORETURN;
+static void help PARAMS ((void)) ATTRIBUTE_NORETURN;
+static void version PARAMS ((void)) ATTRIBUTE_NORETURN;
+static int overloaded_jni_method_exists_p PARAMS ((const unsigned char *, int,
+                                                  const char *, int));
+static void jni_print_char PARAMS ((FILE *, int));
 
 JCF_u2 current_field_name;
 JCF_u2 current_field_value;
@@ -125,12 +169,12 @@ static int method_pass;
 #define HANDLE_END_FIELD()                                                   \
   if (field_pass)                                                            \
     {                                                                        \
-      if (out)                                                               \
+      if (out && ! stubs && ! flag_jni)                                              \
        print_field_info (out, jcf, current_field_name,                       \
                          current_field_signature,                            \
-                         current_field_flags);                               \
+                         current_field_flags);                               \
     }                                                                        \
-  else                                                                       \
+  else if (! stubs && ! flag_jni)                                            \
     add_class_decl (out, jcf, current_field_signature);
 
 #define HANDLE_CONSTANTVALUE(VALUEINDEX) current_field_value = (VALUEINDEX)
@@ -145,31 +189,36 @@ static int method_printed = 0;
       if (out)                                                               \
         print_method_info (out, jcf, NAME, SIGNATURE, ACCESS_FLAGS);         \
     }                                                                        \
-  else                                                                       \
-    add_class_decl (out, jcf, SIGNATURE);
+  else                                                                       \
+    {                                                                        \
+      print_method_info (NULL, jcf, NAME, SIGNATURE, ACCESS_FLAGS);          \
+      if (! stubs && ! flag_jni)                                             \
+       add_class_decl (out, jcf, SIGNATURE);                                 \
+    }
 
 #define HANDLE_CODE_ATTRIBUTE(MAX_STACK, MAX_LOCALS, CODE_LENGTH) \
   if (out && method_declared) decompile_method (out, jcf, CODE_LENGTH);
 
 static int decompiled = 0;
 #define HANDLE_END_METHOD() \
-  if (out && method_printed) fputs (decompiled ? "\n" : ";\n", out);
+  if (out && method_printed) fputs (decompiled || stubs ? "\n" : ";\n", out);
 
 #include "jcf-reader.c"
 
 /* Some useful constants.  */
 #define F_NAN_MASK 0x7f800000
+#if (1 == HOST_FLOAT_WORDS_BIG_ENDIAN) && ! defined (HOST_WORDS_BIG_ENDIAN)
+#define D_NAN_MASK 0x000000007ff00000LL
+#else
 #define D_NAN_MASK 0x7ff0000000000000LL
+#endif
 
 /* Return 1 if F is not Inf or NaN.  */
 static int
 java_float_finite (f)
      jfloat f;
 {
-  union {
-    jfloat f;
-    int32 i;
-  } u;
+  union Word u;
   u.f = f;
 
   /* We happen to know that F_NAN_MASK will match all NaN values, and
@@ -183,25 +232,77 @@ static int
 java_double_finite (d)
      jdouble d;
 {
-  union {
-    jdouble d;
-    int64 i;
-  } u;
+  union DWord u;
   u.d = d;
 
   /* Now check for all NaNs.  */
-  return (u.i & D_NAN_MASK) != D_NAN_MASK;
+  return (u.l & D_NAN_MASK) != D_NAN_MASK;
+}
+
+/* Print a character, appropriately mangled for JNI.  */
+
+static void
+jni_print_char (stream, ch)
+     FILE *stream;
+     int ch;
+{
+  if (! flag_jni)
+    jcf_print_char (stream, ch);
+  else if (ch == '(' || ch == ')')
+    {
+      /* Ignore.  */
+    }
+  else if (ch == '_')
+    fputs ("_1", stream);
+  else if (ch == ';')
+    fputs ("_2", stream);
+  else if (ch == '[')
+    fputs ("_3", stream);
+  else if (ch == '/')
+    fputs ("_", stream);
+  else if ((ch >= '0' && ch <= '9')
+          || (ch >= 'a' && ch <= 'z')
+          || (ch >= 'A' && ch <= 'Z'))
+    fputc (ch, stream);
+  else
+    {
+      /* "Unicode" character.  */
+      fprintf (stream, "_0%04x", ch);
+    }
 }
 
-void
+/* Print a name from the class data.  If the index does not point to a
+   string, an error results.  */
+
+static void
 DEFUN(print_name, (stream, jcf, name_index),
       FILE* stream AND JCF* jcf AND int name_index)
 {
   if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
-    fprintf (stream, "<not a UTF8 constant>");
-  else
+    {
+      fprintf (stream, "<not a UTF8 constant>");
+      found_error = 1;
+    }
+  else if (! flag_jni)
     jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf, name_index),
                    JPOOL_UTF_LENGTH (jcf, name_index));
+  else
+    {
+      /* For JNI we must correctly quote each character.  */
+      const unsigned char *str = JPOOL_UTF_DATA (jcf, name_index);
+      int length = JPOOL_UTF_LENGTH (jcf, name_index);
+      const unsigned char *limit = str + length;
+      while (str < limit)
+       {
+         int ch = UTF8_GET (str, limit);
+         if (ch < 0)
+           {
+             fprintf (stream, "\\<invalid>");
+             return;
+           }
+         jni_print_char (stream, ch);
+       }
+    }
 }
 
 /* Print base name of class.  The base name is everything after the
@@ -215,7 +316,7 @@ print_base_classname (stream, jcf, index)
 {
   int name_index = JPOOL_USHORT1 (jcf, index);
   int len;
-  unsigned char *s, *p, *limit;
+  const unsigned char *s, *p, *limit;
 
   s = JPOOL_UTF_DATA (jcf, name_index);
   len = JPOOL_UTF_LENGTH (jcf, name_index);
@@ -238,29 +339,102 @@ print_base_classname (stream, jcf, index)
     }
 }
 
-/* Return 0 if NAME is equal to STR, nonzero otherwise.  */
+/* Return 0 if NAME is equal to STR, -1 if STR is "less" than NAME,
+   and 1 if STR is "greater" than NAME.  */
 
 static int
 utf8_cmp (str, length, name)
-     unsigned char *str;
+     const unsigned char *str;
      int length;
-     char *name;
+     const char *name;
 {
-  unsigned char *limit = str + length;
+  const unsigned char *limit = str + length;
   int i;
 
   for (i = 0; name[i]; ++i)
     {
       int ch = UTF8_GET (str, limit);
       if (ch != name[i])
-       return 1;
+       return ch - name[i];
     }
 
-  return str != limit;
+  return str == limit ? 0 : 1;
+}
+
+/* This is a sorted list of all C++ keywords.  */
+
+static const char *cxx_keywords[] =
+{
+  "asm",
+  "auto",
+  "bool",
+  "const_cast",
+  "delete",
+  "dynamic_cast",
+  "enum",
+  "explicit",
+  "extern",
+  "friend",
+  "inline",
+  "mutable",
+  "namespace",
+  "overload",
+  "register",
+  "reinterpret_cast",
+  "signed",
+  "sizeof",
+  "static_cast",
+  "struct",
+  "template",
+  "typedef",
+  "typeid",
+  "typename",
+  "typenameopt",
+  "union",
+  "unsigned",
+  "using",
+  "virtual",
+  "volatile",
+  "wchar_t"
+};
+
+
+/* If NAME is the name of a C++ keyword, then return an override name.
+   This is a name that can be used in place of the keyword.
+   Otherwise, return NULL.  The return value is malloc()d.  */
+
+static char *
+cxx_keyword_subst (str, length)
+     const unsigned char *str;
+     int length;
+{
+  int last = sizeof (cxx_keywords) / sizeof (const char *);
+  int first = 0;
+  int mid = (last + first) / 2;
+  int old = -1;
+
+  for (mid = (last + first) / 2;
+       mid != old;
+       old = mid, mid = (last + first) / 2)
+    {
+      int r = utf8_cmp (str, length, cxx_keywords[mid]);
+
+      if (r == 0)
+       {
+         char *str = xmalloc (9 + strlen (cxx_keywords[mid]));
+         strcpy (str, "__dummy_");
+         strcat (str, cxx_keywords[mid]);
+         return str;
+       }
+      else if (r < 0)
+       last = mid;
+      else
+       first = mid;
+    }
+  return NULL;
 }
 
-/* Generate an access control keyword based on FLAGS.  Returns 0 if
-   FLAGS matches the saved access information, nonzero otherwise.  */
+/* Generate an access control keyword based on FLAGS.  */
 
 static void
 generate_access (stream, flags)
@@ -296,7 +470,7 @@ generate_access (stream, flags)
 /* See if NAME is already the name of a method.  */
 static int
 name_is_method_p (name, length)
-     unsigned char *name;
+     const unsigned char *name;
      int length;
 {
   struct method_name *p;
@@ -309,6 +483,83 @@ name_is_method_p (name, length)
   return 0;
 }
 
+/* If there is already a method named NAME, whose signature is not
+   SIGNATURE, then return true.  Otherwise return false.  */
+static int
+overloaded_jni_method_exists_p (name, length, signature, sig_length)
+     const unsigned char *name;
+     int length;
+     const char *signature;
+     int sig_length;
+{
+  struct method_name *p;
+
+  for (p = method_name_list; p != NULL; p = p->next)
+    {
+      if (p->length == length
+         && ! memcmp (p->name, name, length)
+         && (p->sig_length != sig_length
+             || memcmp (p->signature, signature, sig_length)))
+       return 1;
+    }
+  return 0;
+}
+
+/* Get name of a field.  This handles renamings due to C++ clash.  */
+static char *
+get_field_name (jcf, name_index, flags)
+     JCF *jcf;
+     int name_index;
+     JCF_u2 flags;
+{
+  unsigned char *name = JPOOL_UTF_DATA (jcf, name_index);
+  int length = JPOOL_UTF_LENGTH (jcf, name_index);
+  char *override;
+
+  if (name_is_method_p (name, length))
+    {
+      /* This field name matches a method.  So override the name with
+        a dummy name.  This is yucky, but it isn't clear what else to
+        do.  FIXME: if the field is static, then we'll be in real
+        trouble.  */
+      if ((flags & ACC_STATIC))
+       {
+         fprintf (stderr, "static field has same name as method\n");
+         found_error = 1;
+         return NULL;
+       }
+
+      override = xmalloc (length + 3);
+      memcpy (override, name, length);
+      strcpy (override + length, "__");
+    }
+  else
+    override = cxx_keyword_subst (name, length);
+
+  return override;
+}
+
+/* Print a field name.  Convenience function for use with
+   get_field_name.  */
+static void
+print_field_name (stream, jcf, name_index, flags)
+     FILE *stream;
+     JCF *jcf;
+     int name_index;
+     JCF_u2 flags;
+{
+  char *override = get_field_name (jcf, name_index, flags);
+
+  if (override)
+    {
+      fputs (override, stream);
+      free (override);
+    }
+  else
+    jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf, name_index),
+                   JPOOL_UTF_LENGTH (jcf, name_index));
+}
+
 static void
 DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
       FILE *stream AND JCF* jcf
@@ -316,26 +567,38 @@ DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
 {
   char *override = NULL;
 
-  if (flags & ACC_FINAL)
+  generate_access (stream, flags);
+  if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
+    {
+      fprintf (stream, "<not a UTF8 constant>");
+      found_error = 1;
+      return;
+    }
+
+  fputs ("  ", out);
+  if ((flags & ACC_STATIC))
+    fputs ("static ", out);
+
+  if ((flags & ACC_FINAL))
     {
       if (current_field_value > 0)
        {
          char buffer[25];
+         int done = 1;
 
-         generate_access (stream, flags);
          switch (JPOOL_TAG (jcf, current_field_value))
            {
            case CONSTANT_Integer:
              {
                jint num;
                int most_negative = 0;
-               fputs ("  static const jint ", out);
-               print_name (out, jcf, name_index);
+               fputs ("const jint ", out);
+               print_field_name (out, jcf, name_index, 0);
                fputs (" = ", out);
                num = JPOOL_INT (jcf, current_field_value);
                /* We single out the most negative number to print
                   specially.  This avoids later warnings from g++.  */
-               if (num == 0x80000000)
+               if (num == (jint) 0x80000000)
                  {
                    most_negative = 1;
                    ++num;
@@ -348,13 +611,13 @@ DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
              {
                jlong num;
                int most_negative = 0;
-               fputs ("  static const jlong ", out);
-               print_name (out, jcf, name_index);
+               fputs ("const jlong ", out);
+               print_field_name (out, jcf, name_index, 0);
                fputs (" = ", out);
                num = JPOOL_LONG (jcf, current_field_value);
                /* We single out the most negative number to print
                    specially..  This avoids later warnings from g++.  */
-               if (num == 0x8000000000000000LL)
+               if (num == (jlong) 0x8000000000000000LL)
                  {
                    most_negative = 1;
                    ++num;
@@ -366,8 +629,8 @@ DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
            case CONSTANT_Float:
              {
                jfloat fnum = JPOOL_FLOAT (jcf, current_field_value);
-               fputs ("  static const jfloat ", out);
-               print_name (out, jcf, name_index);
+               fputs ("const jfloat ", out);
+               print_field_name (out, jcf, name_index, 0);
                if (! java_float_finite (fnum))
                  fputs (";\n", out);
                else
@@ -377,8 +640,8 @@ DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
            case CONSTANT_Double:
              {
                jdouble dnum = JPOOL_DOUBLE (jcf, current_field_value);
-               fputs ("  static const jdouble ", out);
-               print_name (out, jcf, name_index);
+               fputs ("const jdouble ", out);
+               print_field_name (out, jcf, name_index, 0);
                if (! java_double_finite (dnum))
                  fputs (";\n", out);
                else
@@ -386,65 +649,38 @@ DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
              }
              break;
            default:
-             fprintf(out, " <<inappropriate constant type>>\n");
-           }
-
-         return;
-       }
-    }
-
-  generate_access (stream, flags);
-  fputs ("  ", out);
-  if ((flags & ACC_STATIC))
-    fputs ("static ", out);
-
-  if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
-    {
-      fprintf (stream, "<not a UTF8 constant>");
-      found_error = 1;
-    }
-  else
-    {
-      unsigned char *name = JPOOL_UTF_DATA (jcf, name_index);
-      int length = JPOOL_UTF_LENGTH (jcf, name_index);
-
-      if (name_is_method_p (name, length))
-       {
-         /* This field name matches a method.  So override the name
-            with a dummy name.  This is yucky, but it isn't clear
-            what else to do.  FIXME: if the field is static, then
-            we'll be in real trouble.  */
-         if ((flags & ACC_STATIC))
-           {
-             fprintf (stderr, "static field has same name as method\n");
-             found_error = 1;
+             /* We can't print this as a constant, but we can still
+                print something sensible.  */
+             done = 0;
+             break;
            }
 
-         override = (char *) malloc (length + 3);
-         memcpy (override, name, length);
-         strcpy (override + length, "__");
+         if (done)
+           return;
        }
     }
 
-  print_c_decl (out, jcf, name_index, sig_index, flags, 0, override);
+  override = get_field_name (jcf, name_index, flags);
+  print_c_decl (out, jcf, name_index, sig_index, 0, override, flags);
   fputs (";\n", out);
 
   if (override)
     free (override);
 }
 
+
 static void
 DEFUN(print_method_info, (stream, jcf, name_index, sig_index, flags),
       FILE *stream AND JCF* jcf
       AND int name_index AND int sig_index AND JCF_u2 flags)
 {
-  unsigned char *str;
+  const unsigned char *str;
   int length, is_init = 0;
-  const char *override = NULL;
+  char *override = NULL;
 
   method_declared = 0;
   method_access = flags;
-  if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
+  if (stream && JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
     fprintf (stream, "<not a UTF8 constant>");
   str = JPOOL_UTF_DATA (jcf, name_index);
   length = JPOOL_UTF_LENGTH (jcf, name_index);
@@ -468,53 +704,85 @@ DEFUN(print_method_info, (stream, jcf, name_index, sig_index, flags),
       else
        return;
     }
-  else
+
+  /* During the first method pass, build a list of method names. This will
+  be used to determine if field names conflict with method names. */
+  if (! stream)
     {
       struct method_name *nn;
 
-      nn = (struct method_name *) malloc (sizeof (struct method_name));
-      nn->name = (char *) malloc (length);
+      nn = (struct method_name *) xmalloc (sizeof (struct method_name));
+      nn->name = (char *) xmalloc (length);
       memcpy (nn->name, str, length);
       nn->length = length;
       nn->next = method_name_list;
+      nn->sig_length = JPOOL_UTF_LENGTH (jcf, sig_index);
+      nn->signature = (char *) xmalloc (nn->sig_length);
+      memcpy (nn->signature, JPOOL_UTF_DATA (jcf, sig_index),
+             nn->sig_length);
       method_name_list = nn;
+      
+      /* The rest of this function doesn't matter. */
+      return;
     }
 
-  /* We can't generate a method whose name is a C++ reserved word.
-     For now the only problem has been `delete'; add more here as
-     required.  We can't just ignore the function, because that will
-     cause incorrect code to be generated if the function is virtual
-     (not only for calls to this function for for other functions
-     after it in the vtbl).  So we give it a dummy name instead.  */
-  if (! utf8_cmp (str, length, "delete"))
+  /* We don't worry about overrides in JNI mode.  */
+  if (! flag_jni)
     {
-      /* If the method is static or final, we can safely skip it.  If
-        we don't skip it then we'll have problems since the mangling
-        will be wrong.  FIXME.  */
-      if (METHOD_IS_FINAL (jcf->access_flags, flags)
-         || (flags & ACC_STATIC))
-       return;
-      override = "__dummy_delete";
+      /* We can't generate a method whose name is a C++ reserved word.
+        We can't just ignore the function, because that will cause
+        incorrect code to be generated if the function is virtual
+        (not only for calls to this function for for other functions
+        after it in the vtbl).  So we give it a dummy name instead.  */
+      override = cxx_keyword_subst (str, length);
+      if (override)
+       {
+         /* If the method is static or final, we can safely skip it.
+            If we don't skip it then we'll have problems since the
+            mangling will be wrong.  FIXME.  */
+         if (METHOD_IS_FINAL (jcf->access_flags, flags)
+             || (flags & ACC_STATIC))
+           {
+             free (override);
+             return;
+           }
+       }
     }
 
-  method_printed = 1;
-  generate_access (stream, flags);
-
-  fputs ("  ", out);
-  if ((flags & ACC_STATIC))
-    fputs ("static ", out);
-  else if (! METHOD_IS_FINAL (jcf->access_flags, flags))
+  if (! stubs && ! flag_jni)
     {
-      /* Don't print `virtual' if we have a constructor.  */
-      if (! is_init)
-       fputs ("virtual ", out);
+      method_printed = 1;
+
+      generate_access (stream, flags);
+      
+      fputs ("  ", out);
+      if ((flags & ACC_STATIC))
+       fputs ("static ", out);
+      else if (! METHOD_IS_FINAL (jcf->access_flags, flags))
+       {
+         /* Don't print `virtual' if we have a constructor.  */
+         if (! is_init)
+           fputs ("virtual ", out);
+       }
+      print_c_decl (out, jcf, name_index, sig_index, is_init, override, flags);
+      
+      if ((flags & ACC_ABSTRACT))
+       fputs (" = 0", out);
+      else
+       method_declared = 1;
     }
-  print_c_decl (out, jcf, name_index, sig_index, flags, is_init, override);
-
-  if ((flags & ACC_ABSTRACT))
-    fputs (" = 0", out);
   else
-    method_declared = 1;
+    {
+      if (METHOD_IS_NATIVE (flags)) 
+       {
+         method_printed = 1;
+         print_stub_or_jni (out, jcf, name_index, sig_index,
+                            is_init, override, flags);
+       }
+    }
+
+  if (override)
+    free (override);
 }
 
 /* Try to decompile a method body.  Right now we just try to handle a
@@ -525,7 +793,7 @@ decompile_method (out, jcf, code_len)
      JCF *jcf;
      int code_len;
 {
-  unsigned char *codes = jcf->read_ptr;
+  const unsigned char *codes = jcf->read_ptr;
   int index;
   uint16 name_and_type, name;
 
@@ -550,7 +818,8 @@ decompile_method (out, jcf, code_len)
       name_and_type = JPOOL_USHORT2 (jcf, index);
       /* FIXME: ensure that tag is CONSTANT_NameAndType.  */
       name = JPOOL_USHORT1 (jcf, name_and_type);
-      print_name (out, jcf, name);
+      /* FIXME: flags.  */
+      print_field_name (out, jcf, name, 0);
       fputs ("; }", out);
       decompiled = 1;
     }
@@ -581,54 +850,99 @@ decompile_method (out, jcf, code_len)
 
 /* Print one piece of a signature.  Returns pointer to next parseable
    character on success, NULL on error.  */
-static unsigned char *
+static const unsigned char *
 decode_signature_piece (stream, signature, limit, need_space)
      FILE *stream;
-     unsigned char *signature, *limit;
+     const unsigned char *signature, *limit;
      int *need_space;
 {
   const char *ctype;
+  int array_depth = 0;
 
   switch (signature[0])
     {
     case '[':
+      /* More spaghetti.  */
+
+    array_loop:
       for (signature++; (signature < limit
                         && *signature >= '0'
                         && *signature <= '9'); signature++)
        ;
       switch (*signature)
        {
-       case 'B': ctype = "jbyteArray";  goto printit;
-       case 'C': ctype = "jcharArray";  goto printit;
-       case 'D': ctype = "jdoubleArray";  goto printit;
-       case 'F': ctype = "jfloatArray";  goto printit;
-       case 'I': ctype = "jintArray";  goto printit;
-       case 'S': ctype = "jshortArray";  goto printit;
-       case 'J': ctype = "jlongArray";  goto printit;
-       case 'Z': ctype = "jbooleanArray";  goto printit;
-       case '[': ctype = "jobjectArray"; goto printit;
+       case 'B':
+         ctype = "jbyteArray";
+         break;
+       case 'C':
+         ctype = "jcharArray";
+         break;
+       case 'D':
+         ctype = "jdoubleArray";
+         break;
+       case 'F':
+         ctype = "jfloatArray";
+         break;
+       case 'I':
+         ctype = "jintArray";
+         break;
+       case 'S':
+         ctype = "jshortArray";
+         break;
+       case 'J':
+         ctype = "jlongArray";
+         break;
+       case 'Z':
+         ctype = "jbooleanArray";
+         break;
+       case '[':
+         /* We have a nested array.  */
+         ++array_depth;
+         if (! flag_jni)
+           fputs ("JArray<", stream);
+         goto array_loop;
+
        case 'L':
-         /* We have to generate a reference to JArray here,
-            so that our output matches what the compiler
-            does.  */
+         /* We have to generate a reference to JArray here, so that
+            our output matches what the compiler does.  */
          ++signature;
-         fputs ("JArray<", stream);
+         /* Space between `<' and `:' to avoid C++ digraphs.  */
+         if (! flag_jni)
+           fputs ("JArray< ::", stream);
          while (signature < limit && *signature != ';')
            {
              int ch = UTF8_GET (signature, limit);
-             if (ch == '/')
-               fputs ("::", stream);
-             else
-               jcf_print_char (stream, ch);
+             if (! flag_jni)
+               {
+                 if (ch == '/')
+                   fputs ("::", stream);
+                 else
+                   jcf_print_char (stream, ch);
+               }
            }
-         fputs (" *> *", stream);
+         if (! flag_jni)
+           fputs (" *> *", stream);
          *need_space = 0;
-         ++signature;
+         ctype = NULL;
          break;
        default:
          /* Unparseable signature.  */
          return NULL;
        }
+
+      /* If the previous iterations left us with something to print,
+        print it.  For JNI, we always print `jobjectArray' in the
+        nested cases.  */
+      if (flag_jni && ctype == NULL)
+       {
+         ctype = "jobjectArray";
+         *need_space = 1;
+       }
+      /* The `printit' case will advance SIGNATURE for us.  If we
+        don't go there, we must advance past the `;' ourselves.  */
+      if (ctype != NULL)
+       goto printit;
+      ++signature;
       break;
 
     case '(':
@@ -646,12 +960,41 @@ decode_signature_piece (stream, signature, limit, need_space)
     case 'Z': ctype = "jboolean";  goto printit;
     case 'V': ctype = "void";  goto printit;
     case 'L':
+      if (flag_jni)
+       {
+         /* We know about certain types and special-case their
+            names.
+            FIXME: something like java.lang.Exception should be
+            printed as `jthrowable', because it is a subclass.  This
+            means that gcjh must read the entire hierarchy and
+            comprehend it.  */
+         if (! strncmp (signature, "Ljava/lang/String;",
+                        sizeof ("Ljava/lang/String;") -1))
+           ctype = "jstring";
+         else if (! strncmp (signature, "Ljava/lang/Class;",
+                             sizeof ("Ljava/lang/Class;") - 1))
+           ctype = "jclass";
+         else if (! strncmp (signature, "Ljava/lang/Throwable;",
+                             sizeof ("Ljava/lang/Throwable;") - 1))
+           ctype = "jthrowable";
+         else if (! strncmp (signature, "Ljava/lang/ref/WeakReference;",
+                             sizeof ("Ljava/lang/ref/WeakReference;") - 1))
+           ctype = "jweak";
+         else
+           ctype = "jobject";
+
+         while (*signature && *signature != ';')
+           ++signature;
+
+         goto printit;
+       }
+      /* Print a leading "::" so we look in the right namespace.  */
+      fputs ("::", stream);
       ++signature;
       while (*signature && *signature != ';')
        {
          int ch = UTF8_GET (signature, limit);
-         /* `$' is the separator for an inner class.  */
-         if (ch == '/' || ch == '$')
+         if (ch == '/')
            fputs ("::", stream);
          else
            jcf_print_char (stream, ch);
@@ -663,7 +1006,7 @@ decode_signature_piece (stream, signature, limit, need_space)
       break;
     default:
       *need_space = 1;
-      jcf_print_char (stream, *signature++);
+      jni_print_char (stream, *signature++);
       break;
     printit:
       signature++;
@@ -672,15 +1015,21 @@ decode_signature_piece (stream, signature, limit, need_space)
       break;
     }
 
+  if (! flag_jni)
+    {
+      while (array_depth-- > 0)
+       fputs ("> *", stream);
+    }
+
   return signature;
 }
 
 static void
-DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, flags, is_init,
-                    name_override),
+DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, is_init,
+                    name_override, flags),
       FILE* stream AND JCF* jcf
-      AND int name_index AND int signature_index AND JCF_u2 flags
-      AND int is_init AND const char *name_override)
+      AND int name_index AND int signature_index
+      AND int is_init AND const char *name_override AND int flags)
 {
   if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8)
     {
@@ -690,12 +1039,12 @@ DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, flags, is_init,
   else
     {
       int length = JPOOL_UTF_LENGTH (jcf, signature_index);
-      unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
-      register  unsigned char *str = str0;
-      unsigned char *limit = str + length;
+      const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
+      register const  unsigned char *str = str0;
+      const unsigned char *limit = str + length;
       int need_space = 0;
       int is_method = str[0] == '(';
-      unsigned char *next;
+      const unsigned char *next;
 
       /* If printing a method, skip to the return signature and print
         that first.  However, there is no return value if this is a
@@ -726,44 +1075,203 @@ DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, flags, is_init,
       /* Now print the name of the thing.  */
       if (need_space)
        fputs (" ", stream);
-      if (name_override)
-       fputs (name_override, stream);
-      else if (name_index)
-       {
-         /* Declare constructors specially.  */
-         if (is_init)
-           print_base_classname (stream, jcf, jcf->this_class);
-         else
-           print_name (stream, jcf, name_index);
-       }
+      print_full_cxx_name (stream, jcf, name_index, 
+                          signature_index, is_init, name_override,
+                          flags);
+    }
+}
 
-      if (is_method)
+/* Print the unqualified method name followed by the signature. */
+static void
+DEFUN(print_full_cxx_name, (stream, jcf, name_index, signature_index,
+                           is_init, name_override, flags),
+      FILE* stream AND JCF* jcf
+      AND int name_index AND int signature_index AND int is_init 
+      AND const char *name_override AND int flags)
+{
+  int length = JPOOL_UTF_LENGTH (jcf, signature_index);
+  const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
+  register const unsigned char *str = str0;
+  const unsigned char *limit = str + length;
+  int need_space = 0;
+  int is_method = str[0] == '(';
+  const unsigned char *next;
+
+  if (name_override)
+    fputs (name_override, stream);
+  else if (name_index)
+    {
+      /* Declare constructors specially.  */
+      if (is_init)
+       print_base_classname (stream, jcf, jcf->this_class);
+      else
+       print_name (stream, jcf, name_index);
+    }
+
+  if (flag_jni)
+    {
+      unsigned char *signature = JPOOL_UTF_DATA (jcf, signature_index);
+      int sig_len = JPOOL_UTF_LENGTH (jcf, signature_index);
+      if (overloaded_jni_method_exists_p (JPOOL_UTF_DATA (jcf, name_index),
+                                         JPOOL_UTF_LENGTH (jcf, name_index),
+                                         signature, sig_len))
        {
-         /* Have a method or a constructor.  Print signature pieces
-            until done.  */
-         fputs (" (", stream);
-         str = str0 + 1;
-         while (str < limit && *str != ')')
+         /* If this method is overloaded by another native method,
+            then include the argument information in the mangled
+            name.  */
+         unsigned char *limit = signature + sig_len;
+         fputs ("__", stream);
+         while (signature < limit)
            {
-             next = decode_signature_piece (stream, str, limit, &need_space);
-             if (! next)
+             int ch = UTF8_GET (signature, limit);
+             jni_print_char (stream, ch);
+             if (ch == ')')
                {
-                 fprintf (stderr, "unparseable signature: `%s'\n", str0);
-                 found_error = 1;
-                 return;
+                 /* Done.  */
+                 break;
                }
+           }
+       }
+    }
+
+  if (is_method)
+    {
+      /* Have a method or a constructor.  Print signature pieces
+        until done.  */
+      fputs (" (", stream);
+
+      str = str0 + 1;
 
-             if (next < limit && *next != ')')
-               fputs (", ", stream);
-             str = next;
+      /* In JNI mode, add extra arguments.  */
+      if (flag_jni)
+       {
+         /* FIXME: it would be nice to know if we are printing a decl
+            or a definition, and only print `env' for the latter.  */
+         fputs ("JNIEnv *env", stream);
+
+         fputs ((flags & ACC_STATIC) ? ", jclass" : ", jobject", stream);
+
+         if (*str != ')')
+           fputs (", ", stream);
+       }
+
+      while (str < limit && *str != ')')
+       {
+         next = decode_signature_piece (stream, str, limit, &need_space);
+         if (! next)
+           {
+             fprintf (stderr, "unparseable signature: `%s'\n", str0);
+             found_error = 1;
+             return;
            }
+         
+         if (next < limit && *next != ')')
+           fputs (", ", stream);
+         str = next;
+       }
+      
+      fputs (")", stream);
+    }
+}
+
+/* This is a helper for print_stub_or_jni.  */
+static void
+DEFUN (print_name_for_stub_or_jni, (stream, jcf, name_index, signature_index,
+                                   is_init, name_override, flags),
+       FILE *stream AND JCF *jcf
+       AND int name_index AND int signature_index
+       AND int is_init AND const char *name_override AND int flags)
+{
+  const char *const prefix = flag_jni ? "Java_" : "";
+  print_cxx_classname (stream, prefix, jcf, jcf->this_class);
+  fputs (flag_jni ? "_" : "::", stream);
+  print_full_cxx_name (stream, jcf, name_index, 
+                      signature_index, is_init, name_override,
+                      flags);
+}
+
+static void
+DEFUN(print_stub_or_jni, (stream, jcf, name_index, signature_index, is_init,
+                         name_override, flags),
+      FILE* stream AND JCF* jcf
+      AND int name_index AND int signature_index
+      AND int is_init AND const char *name_override AND int flags)
+{
+  if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8)
+    {
+      fprintf (stream, "<not a UTF8 constant>");
+      found_error = 1;
+    }
+  else
+    {
+      int length = JPOOL_UTF_LENGTH (jcf, signature_index);
+      const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
+      register const unsigned char *str = str0;
+      const unsigned char *limit = str + length;
+      int need_space = 0;
+      int is_method = str[0] == '(';
+      const unsigned char *next;
+
+      /* Don't print fields in the JNI case.  */
+      if (! is_method && flag_jni)
+       return;
+
+      if (flag_jni && ! stubs)
+       fputs ("extern ", stream);
+
+      /* If printing a method, skip to the return signature and print
+        that first.  However, there is no return value if this is a
+        constructor.  */
+      if (is_method && ! is_init)
+       {
+         while (str < limit)
+           {
+             int ch = *str++;
+             if (ch == ')')
+               break;
+           }
+       }
+
+      /* If printing a field or an ordinary method, then print the
+        "return value" now.  Note that a constructor can't be native,
+        so we don't bother checking this in the JNI case.  */
+      if (! is_method || ! is_init)
+       {
+         next = decode_signature_piece (stream, str, limit, &need_space);
+         if (! next)
+           {
+             fprintf (stderr, "unparseable signature: `%s'\n", str0);
+             found_error = 1;
+             return;
+           }
+       }
+
+      /* When printing a JNI header we need to respect the space.  In
+        other cases we're just going to insert a newline anyway.  */
+      fputs (need_space && ! stubs ? " " : "\n", stream);
+      
+      /* Now print the name of the thing.  */
+      print_name_for_stub_or_jni (stream, jcf, name_index,
+                                 signature_index, is_init, name_override,
+                                 flags);
 
-         fputs (")", stream);
+      /* Print the body.  */
+      if (stubs)
+       {
+         if (flag_jni)
+           fputs ("\n{\n  (*env)->FatalError (\"", stream);
+         else
+           fputs ("\n{\n  JvFail (\"", stream);
+         print_name_for_stub_or_jni (stream, jcf, name_index,
+                                     signature_index, is_init,
+                                     name_override,
+                                     flags);
+         fputs (" not implemented\");\n}\n\n", stream);
        }
     }
 }
 
-void
+static void
 DEFUN(print_mangled_classname, (stream, jcf, prefix, index),
       FILE *stream AND JCF *jcf AND const char *prefix AND int index)
 {
@@ -781,13 +1289,13 @@ DEFUN(print_mangled_classname, (stream, jcf, prefix, index),
 static int
 print_cxx_classname (stream, prefix, jcf, index)
      FILE *stream;
-     char *prefix;
+     const char *prefix;
      JCF *jcf;
      int index;
 {
   int name_index = JPOOL_USHORT1 (jcf, index);
   int len, c;
-  unsigned char *s, *p, *limit;
+  const unsigned char *s, *p, *limit;
 
   s = JPOOL_UTF_DATA (jcf, name_index);
   len = JPOOL_UTF_LENGTH (jcf, name_index);
@@ -800,13 +1308,18 @@ print_cxx_classname (stream, prefix, jcf, index)
     return 0;
 
   fputs (prefix, stream);
+
+  /* Print a leading "::" so we look in the right namespace.  */
+  if (! flag_jni && ! stubs)
+    fputs ("::", stream);
+
   while (s < limit)
     {
       c = UTF8_GET (s, limit);
       if (c == '/')
-       fputs ("::", stream);
+       fputs (flag_jni ? "_" : "::", stream);
       else
-       jcf_print_char (stream, c);
+       jni_print_char (stream, c);
     }
 
   return 1;
@@ -816,14 +1329,15 @@ int written_class_count = 0;
 
 /* Return name of superclass.  If LEN is not NULL, fill it with length
    of name.  */
-static unsigned char *
+static const unsigned char *
 super_class_name (derived_jcf, len)
      JCF *derived_jcf;
      int *len;
 {
   int supername_index = JPOOL_USHORT1 (derived_jcf, derived_jcf->super_class);
   int supername_length = JPOOL_UTF_LENGTH (derived_jcf, supername_index);
-  unsigned char *supername = JPOOL_UTF_DATA (derived_jcf, supername_index);
+  const unsigned char *supername =
+    JPOOL_UTF_DATA (derived_jcf, supername_index);
 
   if (len)
     *len = supername_length;
@@ -848,7 +1362,7 @@ static struct include *all_includes = NULL;
 static void
 print_include (out, utf8, len)
      FILE *out;
-     unsigned char *utf8;
+     const unsigned char *utf8;
      int len;
 {
   struct include *incl;
@@ -861,19 +1375,23 @@ print_include (out, utf8, len)
 
   for (incl = all_includes; incl; incl = incl->next)
     {
-      if (! strncmp (incl->name, utf8, len))
+      /* We check the length because we might have a proper prefix.  */
+      if (len == (int) strlen (incl->name)
+         && ! strncmp (incl->name, utf8, len))
        return;
     }
 
-  incl = (struct include *) malloc (sizeof (struct include));
-  incl->name = malloc (len + 1);
+  incl = (struct include *) xmalloc (sizeof (struct include));
+  incl->name = xmalloc (len + 1);
   strncpy (incl->name, utf8, len);
   incl->name[len] = '\0';
   incl->next = all_includes;
   all_includes = incl;
 
   fputs ("#include <", out);
-  jcf_print_utf8 (out, utf8, len);
+  jcf_print_utf8_replace (out, utf8, len,
+                         '/',
+                         flag_jni ? '_' : '/');
   fputs (".h>\n", out);
 }
 
@@ -892,6 +1410,10 @@ struct namelet
   struct namelet *next;
 };
 
+static void add_namelet PARAMS ((const unsigned char *,
+                               const unsigned char *, struct namelet *));
+static void print_namelet PARAMS ((FILE *, struct namelet *, int));
+
 /* The special root namelet.  */
 static struct namelet root =
 {
@@ -906,10 +1428,10 @@ static struct namelet root =
    recursively.  */
 static void
 add_namelet (name, name_limit, parent)
-     unsigned char *name, *name_limit;
+     const unsigned char *name, *name_limit;
      struct namelet *parent;
 {
-  unsigned char *p;
+  const unsigned char *p;
   struct namelet *n = NULL, *np;
 
   /* We want to skip the standard namespaces that we assume the
@@ -920,11 +1442,11 @@ add_namelet (name, name_limit, parent)
 #define JAVALANG "java/lang/"
 #define JAVAIO "java/io/"
 #define JAVAUTIL "java/util/"
-      if ((name_limit - name >= sizeof (JAVALANG) - 1
+      if ((name_limit - name >= (int) sizeof (JAVALANG) - 1
           && ! strncmp (name, JAVALANG, sizeof (JAVALANG) - 1))
-         || (name_limit - name >= sizeof (JAVAUTIL) - 1
+         || (name_limit - name >= (int) sizeof (JAVAUTIL) - 1
              && ! strncmp (name, JAVAUTIL, sizeof (JAVAUTIL) - 1))
-         || (name_limit - name >= sizeof (JAVAIO) - 1
+         || (name_limit - name >= (int) sizeof (JAVAIO) - 1
              && ! strncmp (name, JAVAIO, sizeof (JAVAIO) - 1)))
        return;
     }
@@ -935,7 +1457,9 @@ add_namelet (name, name_limit, parent)
   /* Search for this name beneath the PARENT node.  */
   for (np = parent->subnamelets; np != NULL; np = np->next)
     {
-      if (! strncmp (name, np->name, p - name))
+      /* We check the length because we might have a proper prefix.  */
+      if ((int) strlen (np->name) == p - name &&
+         ! strncmp (name, np->name, p - name))
        {
          n = np;
          break;
@@ -944,8 +1468,8 @@ add_namelet (name, name_limit, parent)
 
   if (n == NULL)
     {
-      n = (struct namelet *) malloc (sizeof (struct namelet));
-      n->name = malloc (p - name + 1);
+      n = (struct namelet *) xmalloc (sizeof (struct namelet));
+      n->name = xmalloc (p - name + 1);
       strncpy (n->name, name, p - name);
       n->name[p - name] = '\0';
       n->is_class = (p == name_limit || *p == '$');
@@ -1002,7 +1526,10 @@ print_namelet (out, name, depth)
        {
          for (i = 0; i < depth; ++i)
            fputc (' ', out);
-         fputs ("};\n", out);
+         fputs ("}\n", out);
+         /* Only print a `;' when printing a class.  C++ is evil.  */
+         if (name->is_class)
+           fputs (";", out);
        }
 
       free (name->name);
@@ -1019,13 +1546,13 @@ add_class_decl (out, jcf, signature)
      JCF *jcf;
      JCF_u2 signature;
 {
-  unsigned char *s = JPOOL_UTF_DATA (jcf, signature);
+  const unsigned char *s = JPOOL_UTF_DATA (jcf, signature);
   int len = JPOOL_UTF_LENGTH (jcf, signature);
   int i;
   /* Name of class we are processing.  */
   int name_index = JPOOL_USHORT1 (jcf, jcf->this_class);
   int tlen = JPOOL_UTF_LENGTH (jcf, name_index);
-  char *tname = JPOOL_UTF_DATA (jcf, name_index);
+  const char *tname = JPOOL_UTF_DATA (jcf, name_index);
 
   for (i = 0; i < len; ++i)
     {
@@ -1034,7 +1561,7 @@ add_class_decl (out, jcf, signature)
       /* If we see an array, then we include the array header.  */
       if (s[i] == '[')
        {
-         print_include (out, "java-array", -1);
+         print_include (out, "gcj/array", -1);
          continue;
        }
 
@@ -1080,7 +1607,7 @@ print_class_decls (out, jcf, self)
      that should be declared.  */
   int name_index = JPOOL_USHORT1 (jcf, self);
   int len;
-  unsigned char *s;
+  const unsigned char *s;
 
   s = JPOOL_UTF_DATA (jcf, name_index);
   len = JPOOL_UTF_LENGTH (jcf, name_index);
@@ -1105,7 +1632,7 @@ DEFUN(process_file, (jcf, out),
   int code, i;
   uint32 field_start, method_end, method_start;
 
-  current_jcf = main_jcf = jcf;
+  current_jcf = jcf;
 
   last_access = -1;
 
@@ -1135,30 +1662,96 @@ DEFUN(process_file, (jcf, out),
   jcf_parse_class (jcf);
 
   if (written_class_count++ == 0 && out)
-    fputs ("// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*-\n\n",
-          out);
+    {
+      const char *cstart, *cstart2, *mode, *cend, *what, *jflag;
+      if (flag_jni)
+       {
+         cstart = "/*";
+         cstart2 = "  ";
+         cend = " */";
+         mode = "";
+         what = "JNI";
+         jflag = " -jni";
+       }
+      else
+       {
+         cstart = "//";
+         cstart2 = "//";
+         cend = "";
+         mode = " -*- c++ -*-";
+         what = "CNI";
+         jflag = "";
+       }
+
+      if (! stubs)
+       fprintf (out, "%s DO NOT EDIT THIS FILE - it is machine generated%s%s\n\n",
+                cstart, mode, cend);
+      else
+       {
+         fprintf (out, "%s This file was created by `gcjh -stubs%s'.%s\n\
+%s\n\
+%s This file is intended to give you a head start on implementing native\n\
+%s methods using %s.\n\
+%s Be aware: running `gcjh -stubs %s' once more for this class may\n\
+%s overwrite any edits you have made to this file.%s\n\n",
+                  cstart, jflag, mode,
+                  cstart2,
+                  cstart2,
+                  cstart2,
+                  what,
+                  cstart2,
+                  jflag,
+                  cstart2,
+                  cend);
+       }
+    }
 
   if (out)
     {
-      print_mangled_classname (out, jcf, "#ifndef __", jcf->this_class);
-      fprintf (out, "__\n");
+      if (! stubs)
+       {
+         print_mangled_classname (out, jcf, "#ifndef __", jcf->this_class);
+         fprintf (out, "__\n");
 
-      print_mangled_classname (out, jcf, "#define __", jcf->this_class);
-      fprintf (out, "__\n\n");
+         print_mangled_classname (out, jcf, "#define __", jcf->this_class);
+         fprintf (out, "__\n\n");
 
-      /* We do this to ensure that inline methods won't be `outlined'
-        by g++.  This works as long as method and fields are not
-        added by the user.  */
-      fprintf (out, "#pragma interface\n");
-    }
+         if (flag_jni)
+           {
+             fprintf (out, "#include <jni.h>\n\n");
+             fprintf (out, "#ifdef __cplusplus\n");
+             fprintf (out, "extern \"C\"\n");
+             fprintf (out, "{\n");
+             fprintf (out, "#endif\n");
+           }
+         else  
+           {
+             /* We do this to ensure that inline methods won't be
+                `outlined' by g++.  This works as long as method and
+                fields are not added by the user.  */
+             fprintf (out, "#pragma interface\n");
 
-  if (jcf->super_class && out)
-    {
-      int super_length;
-      unsigned char *supername = super_class_name (jcf, &super_length);
+             if (jcf->super_class)
+               {
+                 int super_length;
+                 const unsigned char *supername =
+                   super_class_name (jcf, &super_length);
 
-      fputs ("\n", out);
-      print_include (out, supername, super_length);
+                 fputs ("\n", out);
+                 print_include (out, supername, super_length);
+               }
+           }
+       }
+      else
+       {
+         /* Strip off the ".class" portion of the name when printing
+            the include file name.  */
+         int len = strlen (jcf->classname);
+         if (len > 6 && ! strcmp (&jcf->classname[len - 6], ".class"))
+           len -= 6;
+         print_include (out, jcf->classname, len);
+         print_include (out, "gcj/cni", -1);
+       }
     }
 
   /* We want to parse the methods first.  But we need to find where
@@ -1175,33 +1768,40 @@ DEFUN(process_file, (jcf, out),
   jcf_parse_methods (jcf);
 
   if (out)
+    fputs ("\n", out);
+
+  if (out && ! flag_jni)
     {
-      fputs ("\n", out);
-      print_class_decls (out, jcf, jcf->this_class);
+      if (! stubs)
+       print_class_decls (out, jcf, jcf->this_class);
 
       for (i = 0; i < prepend_count; ++i)
        fprintf (out, "%s\n", prepend_specs[i]);
       if (prepend_count > 0)
        fputc ('\n', out);
-    }
 
-  if (out && ! print_cxx_classname (out, "class ", jcf, jcf->this_class))
-    {
-      fprintf (stderr, "class is of array type\n");
-      found_error = 1;
-      return;
-    }
-  if (out && jcf->super_class)
-    {
-      if (! print_cxx_classname (out, " : public ", jcf, jcf->super_class))
+      if (! stubs)
        {
-         fprintf (stderr, "base class is of array type\n");
-         found_error = 1;
-         return;
+         if (! print_cxx_classname (out, "class ", jcf, jcf->this_class))
+           {
+             fprintf (stderr, "class is of array type\n");
+             found_error = 1;
+             return;
+           }
+         if (jcf->super_class)
+           {
+             if (! print_cxx_classname (out, " : public ", 
+                                        jcf, jcf->super_class))
+               {
+                 fprintf (stderr, "base class is of array type\n");
+                 found_error = 1;
+                 return;
+               }
+           }
+
+         fputs ("\n{\n", out);
        }
     }
-  if (out)
-    fputs ("\n{\n", out);
 
   /* Now go back for second pass over methods and fields.  */
   JCF_SEEK (jcf, method_start);
@@ -1216,34 +1816,87 @@ DEFUN(process_file, (jcf, out),
 
   jcf_parse_final_attributes (jcf);
 
-  if (out)
+  if (out && ! stubs)
     {
-      /* Generate friend decl if we still must.  */
-      for (i = 0; i < friend_count; ++i)
-       fprintf (out, "  friend %s\n", friend_specs[i]);
-
-      /* Generate extra declarations.  */
-      if (add_count > 0)
-       fputc ('\n', out);
-      for (i = 0; i < add_count; ++i)
-       fprintf (out, "  %s\n", add_specs[i]);
-
-      fputs ("};\n", out);
-
-      if (append_count > 0)
-       fputc ('\n', out);
-      for (i = 0; i < append_count; ++i)
-       fprintf (out, "%s\n", append_specs[i]);
+      if (flag_jni)
+       {
+             fprintf (out, "\n#ifdef __cplusplus\n");
+             fprintf (out, "}\n");
+             fprintf (out, "#endif\n");
+       }
+      else
+       {
+         /* Generate friend decl if we still must.  */
+         for (i = 0; i < friend_count; ++i)
+           fprintf (out, "  friend %s\n", friend_specs[i]);
+
+         /* Generate extra declarations.  */
+         if (add_count > 0)
+           fputc ('\n', out);
+         for (i = 0; i < add_count; ++i)
+           fprintf (out, "  %s\n", add_specs[i]);
+
+         if (! stubs)
+           fputs ("};\n", out);
+
+         if (append_count > 0)
+           fputc ('\n', out);
+         for (i = 0; i < append_count; ++i)
+           fprintf (out, "%s\n", append_specs[i]);
+       }
 
-      print_mangled_classname (out, jcf, "\n#endif /* __", jcf->this_class);
+      print_mangled_classname (out, jcf, 
+                              "\n#endif /* __", jcf->this_class);
       fprintf (out, "__ */\n");
     }
 }
 
+\f
+
+/* This is used to mark options with no short value.  */
+#define LONG_OPT(Num)  ((Num) + 128)
+
+#define OPT_classpath LONG_OPT (0)
+#define OPT_CLASSPATH LONG_OPT (1)
+#define OPT_HELP      LONG_OPT (2)
+#define OPT_TEMP      LONG_OPT (3)
+#define OPT_VERSION   LONG_OPT (4)
+#define OPT_PREPEND   LONG_OPT (5)
+#define OPT_FRIEND    LONG_OPT (6)
+#define OPT_ADD       LONG_OPT (7)
+#define OPT_APPEND    LONG_OPT (8)
+#define OPT_M         LONG_OPT (9)
+#define OPT_MM        LONG_OPT (10)
+#define OPT_MG        LONG_OPT (11)
+#define OPT_MD        LONG_OPT (12)
+#define OPT_MMD       LONG_OPT (13)
+
+static struct option options[] =
+{
+  { "classpath", required_argument, NULL, OPT_classpath },
+  { "CLASSPATH", required_argument, NULL, OPT_CLASSPATH },
+  { "help",      no_argument,       NULL, OPT_HELP },
+  { "stubs",     no_argument,       &stubs, 1 },
+  { "td",        required_argument, NULL, OPT_TEMP },
+  { "verbose",   no_argument,       NULL, 'v' },
+  { "version",   no_argument,       NULL, OPT_VERSION },
+  { "prepend",   required_argument, NULL, OPT_PREPEND },
+  { "friend",    required_argument, NULL, OPT_FRIEND },
+  { "add",       required_argument, NULL, OPT_ADD },
+  { "append",    required_argument, NULL, OPT_APPEND },
+  { "M",         no_argument,       NULL, OPT_M   },
+  { "MM",        no_argument,       NULL, OPT_MM  },
+  { "MG",        no_argument,       NULL, OPT_MG  },
+  { "MD",        no_argument,       NULL, OPT_MD  },
+  { "MMD",       no_argument,       NULL, OPT_MMD },
+  { "jni",       no_argument,       &flag_jni, 1 },
+  { NULL,        no_argument,       NULL, 0 }
+};
+
 static void
 usage ()
 {
-  fprintf (stderr, "gcjh: no classes specified\n");
+  fprintf (stderr, "Try `gcjh --help' for more information.\n");
   exit (1);
 }
 
@@ -1252,33 +1905,43 @@ help ()
 {
   printf ("Usage: gcjh [OPTION]... CLASS...\n\n");
   printf ("Generate C++ header files from .class files\n\n");
+  printf ("  -stubs                  Generate an implementation stub file\n");
+  printf ("  -jni                    Generate a JNI header or stub\n");
+  printf ("\n");
+  printf ("  -add TEXT               Insert TEXT into class body\n");
+  printf ("  -append TEXT            Insert TEXT after class declaration\n");
+  printf ("  -friend TEXT            Insert TEXT as `friend' declaration\n");
+  printf ("  -prepend TEXT           Insert TEXT before start of class\n");
+  printf ("\n");
   printf ("  --classpath PATH        Set path to find .class files\n");
   printf ("  --CLASSPATH PATH        Set path to find .class files\n");
   printf ("  -IDIR                   Append directory to class path\n");
   printf ("  -d DIRECTORY            Set output directory name\n");
-  printf ("  --help                  Print this help, then exit\n");
   printf ("  -o FILE                 Set output file name\n");
   printf ("  -td DIRECTORY           Set temporary directory name\n");
-  printf ("  -v, --verbose           Print extra information while running\n");
+  printf ("\n");
+  printf ("  --help                  Print this help, then exit\n");
   printf ("  --version               Print version number, then exit\n");
-  /* FIXME: print bug-report information.  */
+  printf ("  -v, --verbose           Print extra information while running\n");
+  printf ("\n");
+  printf ("  -M                      Print all dependencies to stdout;\n");
+  printf ("                             suppress ordinary output\n");
+  printf ("  -MM                     Print non-system dependencies to stdout;\n");
+  printf ("                             suppress ordinary output\n");
+  printf ("  -MD                     Print all dependencies to stdout\n");
+  printf ("  -MMD                    Print non-system dependencies to stdout\n");
+  /* We omit -MG until it is implemented.  */
+  printf ("\n");
+  printf ("For bug reporting instructions, please see:\n");
+  printf ("%s.\n", GCCBUGURL);
   exit (0);
 }
 
 static void
-java_no_argument (opt)
-     char *opt;
-{
-  fprintf (stderr, "gcjh: no argument given for option `%s'\n", opt);
-  exit (1);
-}
-
-static void
 version ()
 {
-  /* FIXME: use version.c?  */
-  printf ("gcjh (GNU gcc) 0.0\n\n");
-  printf ("Copyright (C) 1998 Free Software Foundation, Inc.\n");
+  printf ("gcjh (%s)\n\n", version_string);
+  printf ("Copyright (C) 1998, 1999 Free Software Foundation, Inc.\n");
   printf ("This is free software; see the source for copying conditions.  There is NO\n");
   printf ("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
   exit (0);
@@ -1292,148 +1955,123 @@ DEFUN(main, (argc, argv),
   int argi;
   char *output_file = NULL;
   int emit_dependencies = 0, suppress_output = 0;
+  int opt;
 
   if (argc <= 1)
-    usage ();
+    {
+      fprintf (stderr, "gcjh: no classes specified\n");
+      usage ();
+    }
 
   jcf_path_init ();
 
-  for (argi = 1; argi < argc; argi++)
+  /* We use getopt_long_only to allow single `-' long options.  For
+     some of our options this is more natural.  */
+  while ((opt = getopt_long_only (argc, argv, "I:d:o:v", options, NULL)) != -1)
     {
-      char *arg = argv[argi];
+      switch (opt)
+       {
+       case 0:
+         /* Already handled.  */
+         break;
 
-      if (arg[0] != '-' || ! strcmp (arg, "--"))
-       break;
+       case 'o':
+         output_file = optarg;
+         break;
 
-      /* Just let all arguments be given in either "-" or "--" form.  */
-      if (arg[1] == '-')
-       ++arg;
+       case 'd':
+         output_directory = optarg;
+         break;
 
-      if (strcmp (arg, "-o") == 0)
-       {
-         if (argi + 1 < argc)
-           output_file = argv[++argi];
-         else
-           java_no_argument (argv[argi]);
-       }
-      else if (strcmp (arg, "-d") == 0)
-       {
-         if (argi + 1 < argc)
-           output_directory = argv[++argi];
-         else
-           java_no_argument (argv[argi]);
-       }
-      else if (strcmp (arg, "-td") == 0)
-       {
-         if (argi + 1 < argc)
-           temp_directory = argv[++argi];
-         else
-           java_no_argument (argv[argi]);
-       }
-      else if (strcmp (arg, "-prepend") == 0)
-       {
-         if (argi + 1 < argc)
-           {
-             if (prepend_count == 0)
-               prepend_specs = (char**) ALLOC ((argc-argi) * sizeof (char*));
-             prepend_specs[prepend_count++] = argv[++argi];
-           }
-         else
-           java_no_argument (argv[argi]);
-       }
-      else if (strcmp (arg, "-friend") == 0)
-       {
-         if (argi + 1 < argc)
-           {
-             if (friend_count == 0)
-               friend_specs = (char**) ALLOC ((argc-argi) * sizeof (char*));
-             friend_specs[friend_count++] = argv[++argi];
-           }
-         else
-           java_no_argument (argv[argi]);
-       }
-      else if (strcmp (arg, "-add") == 0)
-       {
-         if (argi + 1 < argc)
-           {
-             if (add_count == 0)
-               add_specs = (char**) ALLOC ((argc-argi) * sizeof (char*));
-             add_specs[add_count++] = argv[++argi];
-           }
-         else
-           java_no_argument (argv[argi]);
-       }
-      else if (strcmp (arg, "-append") == 0)
-       {
-         if (argi + 1 < argc)
-           {
-             if (append_count == 0)
-               append_specs = (char**) ALLOC ((argc-argi) * sizeof (char*));
-             append_specs[append_count++] = argv[++argi];
-           }
-         else
-           java_no_argument (argv[argi]);
-       }
-      else if (strcmp (arg, "-classpath") == 0)
-       {
-         if (argi + 1 < argc)
-           jcf_path_classpath_arg (argv[++argi]);
-         else
-           java_no_argument (argv[argi]);
-       }
-      else if (strcmp (arg, "-CLASSPATH") == 0)
-       {
-         if (argi + 1 < argc)
-           jcf_path_CLASSPATH_arg (argv[++argi]);
-         else
-           java_no_argument (argv[argi]);
-       }
-      else if (strncmp (arg, "-I", 2) == 0)
-       jcf_path_include_arg (arg + 2);
-      else if (strcmp (arg, "-verbose") == 0 || strcmp (arg, "-v") == 0)
-       verbose++;
-      else if (strcmp (arg, "-stubs") == 0)
-       stubs++;
-      else if (strcmp (arg, "-help") == 0)
-       help ();
-      else if (strcmp (arg, "-version") == 0)
-       version ();
-      else if (strcmp (arg, "-M") == 0)
-       {
+       case 'I':
+         jcf_path_include_arg (optarg);
+         break;
+
+       case 'v':
+         verbose++;
+         break;
+
+       case OPT_classpath:
+         jcf_path_classpath_arg (optarg);
+         break;
+
+       case OPT_CLASSPATH:
+         jcf_path_CLASSPATH_arg (optarg);
+         break;
+
+       case OPT_HELP:
+         help ();
+         break;
+
+       case OPT_TEMP:
+         temp_directory = optarg;
+         break;
+
+       case OPT_VERSION:
+         version ();
+         break;
+
+       case OPT_PREPEND:
+         if (prepend_count == 0)
+           prepend_specs = (char**) ALLOC (argc * sizeof (char*));
+         prepend_specs[prepend_count++] = optarg;
+         break;
+
+       case OPT_FRIEND:
+         if (friend_count == 0)
+           friend_specs = (char**) ALLOC (argc * sizeof (char*));
+         friend_specs[friend_count++] = optarg;
+         break;
+
+       case OPT_ADD:
+         if (add_count == 0)
+           add_specs = (char**) ALLOC (argc * sizeof (char*));
+         add_specs[add_count++] = optarg;
+         break;
+
+       case OPT_APPEND:
+         if (append_count == 0)
+           append_specs = (char**) ALLOC (argc * sizeof (char*));
+         append_specs[append_count++] = optarg;
+         break;
+
+       case OPT_M:
          emit_dependencies = 1;
          suppress_output = 1;
          jcf_dependency_init (1);
-       }
-      else if (strcmp (arg, "-MM") == 0)
-       {
+         break;
+
+       case OPT_MM:
          emit_dependencies = 1;
          suppress_output = 1;
          jcf_dependency_init (0);
-       }
-      else if (strcmp (arg, "-MG") == 0)
-       {
-         fprintf (stderr, "gcjh: `%s' option is unimplemented\n", argv[argi]);
+         break;
+
+       case OPT_MG:
+         fprintf (stderr, "gcjh: `-MG' option is unimplemented\n");
          exit (1);
-       }
-      else if (strcmp (arg, "-MD") == 0)
-       {
+
+       case OPT_MD:
          emit_dependencies = 1;
          jcf_dependency_init (1);
-       }
-      else if (strcmp (arg, "-MMD") == 0)
-       {
+         break;
+
+       case OPT_MMD:
          emit_dependencies = 1;
          jcf_dependency_init (0);
-       }
-      else
-       {
-         fprintf (stderr, "%s: illegal argument\n", argv[argi]);
-         exit (1);
+         break;
+
+       default:
+         usage ();
+         break;
        }
     }
 
-  if (argi == argc)
-    usage ();
+  if (optind == argc)
+    {
+      fprintf (stderr, "gcjh: no classes specified\n");
+      usage ();
+    }
 
   jcf_path_seal ();
 
@@ -1443,10 +2081,11 @@ DEFUN(main, (argc, argv),
       exit (1);
     }
 
-  for (; argi < argc; argi++)
+  for (argi = optind; argi < argc; argi++)
     {
       char *classname = argv[argi];
-      char *classfile_name, *current_output_file;
+      char *current_output_file;
+      const char *classfile_name;
 
       if (verbose)
        fprintf (stderr, "Processing %s\n", classname);
@@ -1479,7 +2118,7 @@ DEFUN(main, (argc, argv),
        {
          int dir_len = strlen (output_directory);
          int i, classname_length = strlen (classname);
-         current_output_file = (char*) ALLOC (dir_len + classname_length + 4);
+         current_output_file = (char*) ALLOC (dir_len + classname_length + 5);
          strcpy (current_output_file, output_directory);
          if (dir_len > 0 && output_directory[dir_len-1] != '/')
            current_output_file[dir_len++] = '/';
@@ -1488,6 +2127,8 @@ DEFUN(main, (argc, argv),
              char ch = classname[i];
              if (ch == '.')
                ch = '/';
+             if (flag_jni && ch == '/')
+               ch = '_';
              current_output_file[dir_len++] = ch;
            }
          if (emit_dependencies)
@@ -1505,7 +2146,8 @@ DEFUN(main, (argc, argv),
                  jcf_dependency_set_dep_file (current_output_file);
                }
            }
-         strcpy (current_output_file + dir_len, ".h");
+         strcpy (current_output_file + dir_len, 
+                 stubs ? (flag_jni ? ".c" : ".cc") : ".h");
          jcf_dependency_set_target (current_output_file);
          if (! suppress_output)
            {
@@ -1529,13 +2171,3 @@ DEFUN(main, (argc, argv),
 
   return found_error;
 }
-
-/* TODO:
-
- * Do whatever the javah -stubs flag does.
-
- * Emit "structure forward declarations" when needed.
-
- * Generate C headers, like javah
-
- */