OSDN Git Service

2002-02-01 H.J. Lu <hjl@gnu.org>
[pf3gnuchains/gcc-fork.git] / libiberty / cp-demangle.c
index 48325e6..01c956b 100644 (file)
@@ -1,7 +1,9 @@
-/* Demangler for IA64 / g++ standard C++ ABI.
-   Copyright (C) 2000 CodeSourcery LLC.
+/* Demangler for IA64 / g++ V3 ABI.
+   Copyright (C) 2000, 2001 Free Software Foundation, Inc.
    Written by Alex Samuel <samuel@codesourcery.com>. 
 
+   This file is part of GNU CC.
+
    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 of the License, or
@@ -18,7 +20,7 @@
 */
 
 /* This file implements demangling of C++ names mangled according to
-   the IA64 / g++ standard C++ ABI.  Use the cp_demangle function to
+   the IA64 / g++ V3 ABI.  Use the cp_demangle function to
    demangle a mangled name, or compile with the preprocessor macro
    STANDALONE_DEMANGLER defined to create a demangling filter
    executable (functionally similar to c++filt, but includes this
@@ -66,6 +68,9 @@
    anonymous namespace.  */
 #define ANONYMOUS_NAMESPACE_PREFIX "_GLOBAL_"
 
+/* Character(s) to use for namespace separation in demangled output */
+#define NAMESPACE_SEPARATOR (dm->style == DMGL_JAVA ? "." : "::")
+
 /* If flag_verbose is zero, some simplifications will be made to the
    output to make it easier to read and supress details that are
    generally not of interest to the average C++ programmer.
@@ -77,13 +82,22 @@ static int flag_verbose;
    specification -- don't demangle special g++ manglings.  */
 static int flag_strict;
 
-/* String_list_t is an extended form of dyn_string_t which provides a link
-   field.  A string_list_t may safely be cast to and used as a
-   dyn_string_t.  */
+/* String_list_t is an extended form of dyn_string_t which provides a
+   link field and a caret position for additions to the string.  A
+   string_list_t may safely be cast to and used as a dyn_string_t.  */
 
 struct string_list_def
 {
+  /* The dyn_string; must be first.  */
   struct dyn_string string;
+
+  /* The position at which additional text is added to this string
+     (using the result_add* macros).  This value is an offset from the
+     end of the string, not the beginning (and should be
+     non-positive).  */
+  int caret_position;
+
+  /* The next string in the list.  */
   struct string_list_def *next;
 };
 
@@ -155,6 +169,18 @@ struct demangling_def
 
   /* The most recently demangled source-name.  */
   dyn_string_t last_source_name;
+  
+  /* Language style to use for demangled output. */
+  int style;
+
+  /* Set to non-zero iff this name is a constructor.  The actual value
+     indicates what sort of constructor this is; see demangle.h.  */
+  enum gnu_v3_ctor_kinds is_constructor;
+
+  /* Set to non-zero iff this name is a destructor.  The actual value
+     indicates what sort of destructor this is; see demangle.h.  */
+  enum gnu_v3_dtor_kinds is_destructor;
+
 };
 
 typedef struct demangling_def *demangling_t;
@@ -170,7 +196,7 @@ typedef const char *status_t;
 #define STATUS_INTERNAL_ERROR           "Internal error."
 
 /* This status code indicates a failure in malloc or realloc.  */
-static const charconst status_allocation_failed = "Allocation failed.";
+static const char *const status_allocation_failed = "Allocation failed.";
 #define STATUS_ALLOCATION_FAILED        status_allocation_failed
 
 /* Non-zero if STATUS indicates that no error has occurred.  */
@@ -229,7 +255,7 @@ static void template_arg_list_print
 static template_arg_list_t current_template_arg_list
   PARAMS ((demangling_t));
 static demangling_t demangling_new
-  PARAMS ((const char *));
+  PARAMS ((const char *, int));
 static void demangling_delete 
   PARAMS ((demangling_t));
 
@@ -270,19 +296,28 @@ static void demangling_delete
 /* Returns the string containing the current demangled result.  */
 #define result_string(DM)       (&(DM)->result->string)
 
-/* Appends a dyn_string_t to the demangled result.  */
-#define result_append_string(DM, STRING)                                \
-  (dyn_string_append (&(DM)->result->string, (STRING))                  \
+/* Returns the position at which new text is inserted into the
+   demangled result.  */
+#define result_caret_pos(DM)                                            \
+  (result_length (DM) +                                                 \
+   ((string_list_t) result_string (DM))->caret_position)
+
+/* Adds a dyn_string_t to the demangled result.  */
+#define result_add_string(DM, STRING)                                   \
+  (dyn_string_insert (&(DM)->result->string,                            \
+                     result_caret_pos (DM), (STRING))                  \
    ? STATUS_OK : STATUS_ALLOCATION_FAILED)
 
-/* Appends NUL-terminated string CSTR to the demangled result.  */
-#define result_append(DM, CSTR)                                         \
-  (dyn_string_append_cstr (&(DM)->result->string, (CSTR))               \
+/* Adds NUL-terminated string CSTR to the demangled result.    */
+#define result_add(DM, CSTR)                                            \
+  (dyn_string_insert_cstr (&(DM)->result->string,                       \
+                          result_caret_pos (DM), (CSTR))               \
    ? STATUS_OK : STATUS_ALLOCATION_FAILED)
 
-/* Appends character CHAR to the demangled result.  */
-#define result_append_char(DM, CHAR)                                    \
-  (dyn_string_append_char (&(DM)->result->string, (CHAR))               \
+/* Adds character CHAR to the demangled result.  */
+#define result_add_char(DM, CHAR)                                       \
+  (dyn_string_insert_char (&(DM)->result->string,                       \
+                          result_caret_pos (DM), (CHAR))               \
    ? STATUS_OK : STATUS_ALLOCATION_FAILED)
 
 /* Inserts a dyn_string_t to the demangled result at position POS.  */
@@ -305,12 +340,6 @@ static void demangling_delete
 #define result_length(DM)                                               \
   dyn_string_length (&(DM)->result->string)
 
-/* Appends a space to the demangled result if the last character is
-   not a space.  */
-#define result_append_space(DM)                                         \
-  (dyn_string_append_space (&(DM)->result->string)                      \
-   ? STATUS_OK : STATUS_ALLOCATION_FAILED)
-
 /* Appends a (less-than, greater-than) character to the result in DM
    to (open, close) a template argument or parameter list.  Appends a
    space first if necessary to prevent spurious elision of angle
@@ -378,6 +407,7 @@ string_list_new (length)
      int length;
 {
   string_list_t s = (string_list_t) malloc (sizeof (struct string_list_def));
+  s->caret_position = 0;
   if (s == NULL)
     return NULL;
   if (!dyn_string_init ((dyn_string_t) s, length))
@@ -394,7 +424,7 @@ string_list_delete (node)
   while (node != NULL)
     {
       string_list_t next = node->next;
-      free (node);
+      dyn_string_delete ((dyn_string_t) node);
       node = next;
     }
 }
@@ -407,20 +437,15 @@ result_add_separated_char (dm, character)
      demangling_t dm;
      int character;
 {
-  dyn_string_t s = &dm->result->string;
-
-  /* Add a space if the last character is already a closing angle
-     bracket, so that a nested template arg list doesn't look like
-     it's closed with a right-shift operator.  */
-  if (dyn_string_last_char (s) == character)
-    {
-      if (!dyn_string_append_char (s, ' '))
-       return STATUS_ALLOCATION_FAILED;
-    }
+  char *result = dyn_string_buf (result_string (dm));
+  int caret_pos = result_caret_pos (dm);
 
-  /* Add closing angle brackets.  */
-  if (!dyn_string_append_char (s, character))
-    return STATUS_ALLOCATION_FAILED;
+  /* Add a space if the last character is already the character we
+     want to add.  */
+  if (caret_pos > 0 && result[caret_pos - 1] == character)
+    RETURN_IF_ERROR (result_add_char (dm, ' '));
+  /* Add the character.  */
+  RETURN_IF_ERROR (result_add_char (dm, character));
 
   return STATUS_OK;
 }
@@ -458,6 +483,51 @@ result_pop (dm)
   return top;
 }
 
+/* Returns the current value of the caret for the result string.  The
+   value is an offet from the end of the result string.  */
+
+static int
+result_get_caret (dm)
+     demangling_t dm;
+{
+  return ((string_list_t) result_string (dm))->caret_position;
+}
+
+/* Sets the value of the caret for the result string, counted as an
+   offet from the end of the result string.  */
+
+static void
+result_set_caret (dm, position)
+     demangling_t dm;
+     int position;
+{
+  ((string_list_t) result_string (dm))->caret_position = position;
+}
+
+/* Shifts the position of the next addition to the result by
+   POSITION_OFFSET.  A negative value shifts the caret to the left.  */
+
+static void
+result_shift_caret (dm, position_offset)
+     demangling_t dm;
+     int position_offset;
+{
+  ((string_list_t) result_string (dm))->caret_position += position_offset;
+}
+
+/* Returns non-zero if the character that comes right before the place
+   where text will be added to the result is a space.  In this case,
+   the caller should supress adding another space.  */
+
+static int
+result_previous_char_is_space (dm)
+     demangling_t dm;
+{
+  char *result = dyn_string_buf (result_string (dm));
+  int pos = result_caret_pos (dm);
+  return pos > 0 && result[pos - 1] == ' ';
+}
+
 /* Returns the start position of a fragment of the demangled result
    that will be a substitution candidate.  Should be called at the
    start of productions that can add substitutions.  */
@@ -466,7 +536,7 @@ static int
 substitution_start (dm)
      demangling_t dm;
 {
-  return result_length (dm);
+  return result_caret_pos (dm);
 }
 
 /* Adds the suffix of the current demangled result of DM starting at
@@ -489,7 +559,7 @@ substitution_add (dm, start_position, template_p)
   /* Extract the substring of the current demangling result that
      represents the subsitution candidate.  */
   if (!dyn_string_substring (substitution, 
-                            result, start_position, result_length (dm)))
+                            result, start_position, result_caret_pos (dm)))
     {
       dyn_string_delete (substitution);
       return STATUS_ALLOCATION_FAILED;
@@ -728,8 +798,9 @@ current_template_arg_list (dm)
    Returns NULL if allocation fails.  */
 
 static demangling_t
-demangling_new (name)
+demangling_new (name, style)
      const char *name;
+     int style;
 {
   demangling_t dm;
   dm = (demangling_t) malloc (sizeof (struct demangling_def));
@@ -752,6 +823,9 @@ demangling_new (name)
       dyn_string_delete (dm->last_source_name);
       return NULL;
     }
+  dm->style = style;
+  dm->is_constructor = 0;
+  dm->is_destructor = 0;
 
   return dm;
 }
@@ -841,7 +915,7 @@ static status_t demangle_bare_function_type
 static status_t demangle_class_enum_type
   PARAMS ((demangling_t, int *));
 static status_t demangle_array_type
-  PARAMS ((demangling_t));
+  PARAMS ((demangling_t, int *));
 static status_t demangle_template_param
   PARAMS ((demangling_t));
 static status_t demangle_template_args
@@ -863,7 +937,7 @@ static status_t demangle_local_name
 static status_t demangle_discriminator 
   PARAMS ((demangling_t, int));
 static status_t cp_demangle
-  PARAMS ((const char *, dyn_string_t));
+  PARAMS ((const char *, dyn_string_t, int));
 #ifdef IN_LIBGCC2
 static status_t cp_demangle_type
   PARAMS ((const char*, dyn_string_t));
@@ -931,7 +1005,7 @@ demangle_encoding (dm)
   
   /* Remember where the name starts.  If it turns out to be a template
      function, we'll have to insert the return type here.  */
-  start_position = result_length (dm);
+  start_position = result_caret_pos (dm);
 
   if (peek == 'G' || peek == 'T')
     RETURN_IF_ERROR (demangle_special_name (dm));
@@ -941,7 +1015,7 @@ demangle_encoding (dm)
       RETURN_IF_ERROR (demangle_name (dm, &encode_return_type));
 
       /* If there's anything left, the name was a function name, with
-        maybe its return type, and its parameters types, following.  */
+        maybe its return type, and its parameter types, following.  */
       if (!end_of_name_p (dm) 
          && peek_char (dm) != 'E')
        {
@@ -1015,7 +1089,7 @@ demangle_name (dm, encode_return_type)
        {
          (void) next_char (dm);
          (void) next_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "std::"));
+         RETURN_IF_ERROR (result_add (dm, "std::"));
          RETURN_IF_ERROR 
            (demangle_unqualified_name (dm, &suppress_return_type));
          is_std_substitution = 1;
@@ -1080,20 +1154,30 @@ demangle_nested_name (dm, encode_return_type)
   peek = peek_char (dm);
   if (peek == 'r' || peek == 'V' || peek == 'K')
     {
+      dyn_string_t cv_qualifiers;
       status_t status;
 
-      /* Snarf up and emit CV qualifiers.  */
-      dyn_string_t cv_qualifiers = dyn_string_new (24);
+      /* Snarf up CV qualifiers.  */
+      cv_qualifiers = dyn_string_new (24);
       if (cv_qualifiers == NULL)
        return STATUS_ALLOCATION_FAILED;
-
       demangle_CV_qualifiers (dm, cv_qualifiers);
-      status = result_append_string (dm, cv_qualifiers);
+
+      /* Emit them, preceded by a space.  */
+      status = result_add_char (dm, ' ');
+      if (STATUS_NO_ERROR (status)) 
+       status = result_add_string (dm, cv_qualifiers);
+      /* The CV qualifiers that occur in a <nested-name> will be
+        qualifiers for member functions.  These are placed at the end
+        of the function.  Therefore, shift the caret to the left by
+        the length of the qualifiers, so other text is inserted
+        before them and they stay at the end.  */
+      result_shift_caret (dm, -dyn_string_length (cv_qualifiers) - 1);
+      /* Clean up.  */
       dyn_string_delete (cv_qualifiers);
       RETURN_IF_ERROR (status);
-      RETURN_IF_ERROR (result_append_space (dm));
     }
-  
+
   RETURN_IF_ERROR (demangle_prefix (dm, encode_return_type));
   /* No need to demangle the final <unqualified-name>; demangle_prefix
      will handle it.  */
@@ -1157,7 +1241,7 @@ demangle_prefix (dm, encode_return_type)
        {
          /* We have another level of scope qualification.  */
          if (nested)
-           RETURN_IF_ERROR (result_append (dm, "::"));
+           RETURN_IF_ERROR (result_add (dm, NAMESPACE_SEPARATOR));
          else
            nested = 1;
 
@@ -1273,7 +1357,7 @@ demangle_source_name (dm)
                                        dm->last_source_name));
 
   /* Emit it.  */
-  RETURN_IF_ERROR (result_append_string (dm, dm->last_source_name));
+  RETURN_IF_ERROR (result_add_string (dm, dm->last_source_name));
 
   return STATUS_OK;
 }
@@ -1473,11 +1557,11 @@ demangle_operator_name (dm, short_name, num_args)
   struct operator_code
   {
     /* The mangled code for this operator.  */
-    const char *code;
+    const char *const code;
     /* The source name of this operator.  */
-    const char *name;
+    const char *const name;
     /* The number of arguments this operator takes.  */
-    int num_args;
+    const int num_args;
   };
 
   static const struct operator_code operators[] = 
@@ -1545,7 +1629,7 @@ demangle_operator_name (dm, short_name, num_args)
   /* Is this a vendor-extended operator?  */
   if (c0 == 'v' && IS_DIGIT (c1))
     {
-      RETURN_IF_ERROR (result_append (dm, "operator "));
+      RETURN_IF_ERROR (result_add (dm, "operator "));
       RETURN_IF_ERROR (demangle_source_name (dm));
       *num_args = 0;
       return STATUS_OK;
@@ -1554,7 +1638,7 @@ demangle_operator_name (dm, short_name, num_args)
   /* Is this a conversion operator?  */
   if (c0 == 'c' && c1 == 'v')
     {
-      RETURN_IF_ERROR (result_append (dm, "operator "));
+      RETURN_IF_ERROR (result_add (dm, "operator "));
       /* Demangle the converted-to type.  */
       RETURN_IF_ERROR (demangle_type (dm));
       *num_args = 0;
@@ -1572,8 +1656,8 @@ demangle_operator_name (dm, short_name, num_args)
        /* Found it.  */
        {
          if (!short_name)
-           RETURN_IF_ERROR (result_append (dm, "operator"));
-         RETURN_IF_ERROR (result_append (dm, p->name));
+           RETURN_IF_ERROR (result_add (dm, "operator"));
+         RETURN_IF_ERROR (result_add (dm, p->name));
          *num_args = p->num_args;
 
          return STATUS_OK;
@@ -1613,11 +1697,11 @@ demangle_nv_offset (dm)
   /* Don't display the offset unless in verbose mode.  */
   if (flag_verbose)
     {
-      status = result_append (dm, " [nv:");
+      status = result_add (dm, " [nv:");
       if (STATUS_NO_ERROR (status))
-       status = result_append_string (dm, number);
+       status = result_add_string (dm, number);
       if (STATUS_NO_ERROR (status))
-       status = result_append_char (dm, ']');
+       status = result_add_char (dm, ']');
     }
 
   /* Clean up.  */
@@ -1649,11 +1733,11 @@ demangle_v_offset (dm)
   /* Don't display the offset unless in verbose mode.  */
   if (flag_verbose)
     {
-      status = result_append (dm, " [v:");
+      status = result_add (dm, " [v:");
       if (STATUS_NO_ERROR (status))
-       status = result_append_string (dm, number);
+       status = result_add_string (dm, number);
       if (STATUS_NO_ERROR (status))
-       result_append_char (dm, ',');
+       result_add_char (dm, ',');
     }
   dyn_string_delete (number);
   RETURN_IF_ERROR (status);
@@ -1670,9 +1754,9 @@ demangle_v_offset (dm)
   /* Don't display the vcall offset unless in verbose mode.  */
   if (flag_verbose)
     {
-      status = result_append_string (dm, number);
+      status = result_add_string (dm, number);
       if (STATUS_NO_ERROR (status))
-       status = result_append_char (dm, ']');
+       status = result_add_char (dm, ']');
     }
   dyn_string_delete (number);
   RETURN_IF_ERROR (status);
@@ -1758,11 +1842,27 @@ demangle_special_name (dm)
 
   if (peek == 'G')
     {
-      /* A guard variable name.  Consume the G.  */
+      /* Consume the G.  */
       advance_char (dm);
-      RETURN_IF_ERROR (demangle_char (dm, 'V'));
-      RETURN_IF_ERROR (result_append (dm, "guard variable for "));
-      RETURN_IF_ERROR (demangle_name (dm, &unused));
+      switch (peek_char (dm))
+       {
+       case 'V':
+         /* A guard variable name.  */
+         advance_char (dm);
+         RETURN_IF_ERROR (result_add (dm, "guard variable for "));
+         RETURN_IF_ERROR (demangle_name (dm, &unused));
+         break;
+
+       case 'R':
+         /* A reference temporary.  */
+         advance_char (dm);
+         RETURN_IF_ERROR (result_add (dm, "reference temporary for "));
+         RETURN_IF_ERROR (demangle_name (dm, &unused));
+         break;
+         
+       default:
+         return "Unrecognized <special-name>.";
+       }
     }
   else if (peek == 'T')
     {
@@ -1776,77 +1876,77 @@ demangle_special_name (dm)
        case 'V':
          /* Virtual table.  */
          advance_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "vtable for "));
+         RETURN_IF_ERROR (result_add (dm, "vtable for "));
          RETURN_IF_ERROR (demangle_type (dm));
          break;
 
        case 'T':
          /* VTT structure.  */
          advance_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "VTT for "));
+         RETURN_IF_ERROR (result_add (dm, "VTT for "));
          RETURN_IF_ERROR (demangle_type (dm));
          break;
 
        case 'I':
          /* Typeinfo structure.  */
          advance_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "typeinfo for "));
+         RETURN_IF_ERROR (result_add (dm, "typeinfo for "));
          RETURN_IF_ERROR (demangle_type (dm));
          break;
 
        case 'F':
          /* Typeinfo function.  Used only in old ABI with new mangling.  */
          advance_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "typeinfo fn for "));
+         RETURN_IF_ERROR (result_add (dm, "typeinfo fn for "));
          RETURN_IF_ERROR (demangle_type (dm));
          break;
 
        case 'S':
          /* Character string containing type name, used in typeinfo. */
          advance_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "typeinfo name for "));
+         RETURN_IF_ERROR (result_add (dm, "typeinfo name for "));
          RETURN_IF_ERROR (demangle_type (dm));
          break;
 
        case 'J':
          /* The java Class variable corresponding to a C++ class.  */
          advance_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "java Class for "));
+         RETURN_IF_ERROR (result_add (dm, "java Class for "));
          RETURN_IF_ERROR (demangle_type (dm));
          break;
 
        case 'h':
          /* Non-virtual thunk.  */
          advance_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "non-virtual thunk"));
+         RETURN_IF_ERROR (result_add (dm, "non-virtual thunk"));
          RETURN_IF_ERROR (demangle_nv_offset (dm));
          /* Demangle the separator.  */
          RETURN_IF_ERROR (demangle_char (dm, '_'));
          /* Demangle and emit the target name and function type.  */
-         RETURN_IF_ERROR (result_append (dm, " to "));
+         RETURN_IF_ERROR (result_add (dm, " to "));
          RETURN_IF_ERROR (demangle_encoding (dm));
          break;
 
        case 'v':
          /* Virtual thunk.  */
          advance_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "virtual thunk"));
+         RETURN_IF_ERROR (result_add (dm, "virtual thunk"));
          RETURN_IF_ERROR (demangle_v_offset (dm));
          /* Demangle the separator.  */
          RETURN_IF_ERROR (demangle_char (dm, '_'));
          /* Demangle and emit the target function.  */
-         RETURN_IF_ERROR (result_append (dm, " to "));
+         RETURN_IF_ERROR (result_add (dm, " to "));
          RETURN_IF_ERROR (demangle_encoding (dm));
          break;
 
        case 'c':
          /* Covariant return thunk.  */
          advance_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "covariant return thunk"));
+         RETURN_IF_ERROR (result_add (dm, "covariant return thunk"));
          RETURN_IF_ERROR (demangle_call_offset (dm));
          RETURN_IF_ERROR (demangle_call_offset (dm));
          /* Demangle and emit the target function.  */
-         RETURN_IF_ERROR (result_append (dm, " to "));
+         RETURN_IF_ERROR (result_add (dm, " to "));
          RETURN_IF_ERROR (demangle_encoding (dm));
          break;
 
@@ -1857,7 +1957,7 @@ demangle_special_name (dm)
              dyn_string_t derived_type;
 
              advance_char (dm);
-             RETURN_IF_ERROR (result_append (dm, "construction vtable for "));
+             RETURN_IF_ERROR (result_add (dm, "construction vtable for "));
 
              /* Demangle the derived type off to the side.  */
              RETURN_IF_ERROR (result_push (dm));
@@ -1881,17 +1981,17 @@ demangle_special_name (dm)
 
              /* Emit the derived type.  */
              if (STATUS_NO_ERROR (status))
-               status = result_append (dm, "-in-");
+               status = result_add (dm, "-in-");
              if (STATUS_NO_ERROR (status))
-               status = result_append_string (dm, derived_type);
+               status = result_add_string (dm, derived_type);
              dyn_string_delete (derived_type);
 
              /* Don't display the offset unless in verbose mode.  */
              if (flag_verbose)
                {
-                 status = result_append_char (dm, ' ');
+                 status = result_add_char (dm, ' ');
                  if (STATUS_NO_ERROR (status))
-                   result_append_string (dm, number);
+                   result_add_string (dm, number);
                }
              dyn_string_delete (number);
              RETURN_IF_ERROR (status);
@@ -1945,33 +2045,51 @@ demangle_ctor_dtor_name (dm)
     {
       /* A constructor name.  Consume the C.  */
       advance_char (dm);
-      if (peek_char (dm) < '1' || peek_char (dm) > '3')
+      flavor = next_char (dm);
+      if (flavor < '1' || flavor > '3')
        return "Unrecognized constructor.";
-      RETURN_IF_ERROR (result_append_string (dm, dm->last_source_name));
+      RETURN_IF_ERROR (result_add_string (dm, dm->last_source_name));
+      switch (flavor)
+       {
+       case '1': dm->is_constructor = gnu_v3_complete_object_ctor;
+         break;
+       case '2': dm->is_constructor = gnu_v3_base_object_ctor;
+         break;
+       case '3': dm->is_constructor = gnu_v3_complete_object_allocating_ctor;
+         break;
+       }
       /* Print the flavor of the constructor if in verbose mode.  */
-      flavor = next_char (dm) - '1';
       if (flag_verbose)
        {
-         RETURN_IF_ERROR (result_append (dm, "["));
-         RETURN_IF_ERROR (result_append (dm, ctor_flavors[flavor]));
-         RETURN_IF_ERROR (result_append_char (dm, ']'));
+         RETURN_IF_ERROR (result_add (dm, "["));
+         RETURN_IF_ERROR (result_add (dm, ctor_flavors[flavor - '1']));
+         RETURN_IF_ERROR (result_add_char (dm, ']'));
        }
     }
   else if (peek == 'D')
     {
       /* A destructor name.  Consume the D.  */
       advance_char (dm);
-      if (peek_char (dm) < '0' || peek_char (dm) > '2')
+      flavor = next_char (dm);
+      if (flavor < '0' || flavor > '2')
        return "Unrecognized destructor.";
-      RETURN_IF_ERROR (result_append_char (dm, '~'));
-      RETURN_IF_ERROR (result_append_string (dm, dm->last_source_name));
+      RETURN_IF_ERROR (result_add_char (dm, '~'));
+      RETURN_IF_ERROR (result_add_string (dm, dm->last_source_name));
+      switch (flavor)
+       {
+       case '0': dm->is_destructor = gnu_v3_deleting_dtor;
+         break;
+       case '1': dm->is_destructor = gnu_v3_complete_object_dtor;
+         break;
+       case '2': dm->is_destructor = gnu_v3_base_object_dtor;
+         break;
+       }
       /* Print the flavor of the destructor if in verbose mode.  */
-      flavor = next_char (dm) - '0';
       if (flag_verbose)
        {
-         RETURN_IF_ERROR (result_append (dm, " ["));
-         RETURN_IF_ERROR (result_append (dm, dtor_flavors[flavor]));
-         RETURN_IF_ERROR (result_append_char (dm, ']'));
+         RETURN_IF_ERROR (result_add (dm, " ["));
+         RETURN_IF_ERROR (result_add (dm, dtor_flavors[flavor - '0']));
+         RETURN_IF_ERROR (result_add_char (dm, ']'));
        }
     }
   else
@@ -2012,7 +2130,6 @@ demangle_type_ptr (dm, insert_pos, substitution_start)
      int *insert_pos;
      int substitution_start;
 {
-  char next;
   status_t status;
   int is_substitution_candidate = 1;
 
@@ -2020,23 +2137,25 @@ demangle_type_ptr (dm, insert_pos, substitution_start)
 
   /* Scan forward, collecting pointers and references into symbols,
      until we hit something else.  Then emit the type.  */
-  next = peek_char (dm);
-  if (next == 'P')
+  switch (peek_char (dm))
     {
+    case 'P':
       /* A pointer.  Snarf the `P'.  */
       advance_char (dm);
       /* Demangle the underlying type.  */
       RETURN_IF_ERROR (demangle_type_ptr (dm, insert_pos, 
                                          substitution_start));
       /* Insert an asterisk where we're told to; it doesn't
-        necessarily go at the end.  */
-      RETURN_IF_ERROR (result_insert_char (dm, *insert_pos, '*'));
+        necessarily go at the end.  If we're doing Java style output, 
+        there is no pointer symbol.  */
+      if (dm->style != DMGL_JAVA)
+       RETURN_IF_ERROR (result_insert_char (dm, *insert_pos, '*'));
       /* The next (outermost) pointer or reference character should go
         after this one.  */
       ++(*insert_pos);
-    }
-  else if (next == 'R')
-    {
+      break;
+
+    case 'R':
       /* A reference.  Snarf the `R'.  */
       advance_char (dm);
       /* Demangle the underlying type.  */
@@ -2048,8 +2167,9 @@ demangle_type_ptr (dm, insert_pos, substitution_start)
       /* The next (outermost) pointer or reference character should go
         after this one.  */
       ++(*insert_pos);
-    }
-  else if (next == 'M')
+      break;
+
+    case 'M':
     {
       /* A pointer-to-member.  */
       dyn_string_t class_type;
@@ -2069,17 +2189,24 @@ demangle_type_ptr (dm, insert_pos, substitution_start)
           and set *insert_pos to the spot between the first
           parentheses.  */
        status = demangle_type_ptr (dm, insert_pos, substitution_start);
+      else if (peek_char (dm) == 'A')
+       /* A pointer-to-member array variable.  We want output that
+          looks like `int (Klass::*) [10]'.  Demangle the array type
+          as `int () [10]', and set *insert_pos to the spot between
+          the parentheses.  */
+       status = demangle_array_type (dm, insert_pos);
       else
         {
          /* A pointer-to-member variable.  Demangle the type of the
              pointed-to member.  */
          status = demangle_type (dm);
          /* Make it pretty.  */
-         if (STATUS_NO_ERROR (status))
-           status = result_append_space (dm);
+         if (STATUS_NO_ERROR (status)
+             && !result_previous_char_is_space (dm))
+           status = result_add_char (dm, ' ');
          /* The pointer-to-member notation (e.g. `C::*') follows the
              member's type.  */
-         *insert_pos = result_length (dm);
+         *insert_pos = result_caret_pos (dm);
        }
 
       /* Build the pointer-to-member notation.  */
@@ -2098,15 +2225,16 @@ demangle_type_ptr (dm, insert_pos, substitution_start)
 
       RETURN_IF_ERROR (status);
     }
-  else if (next == 'F')
-    {
+    break;
+
+    case 'F':
       /* Ooh, tricky, a pointer-to-function.  When we demangle the
         function type, the return type should go at the very
         beginning.  */
-      *insert_pos = result_length (dm);
+      *insert_pos = result_caret_pos (dm);
       /* The parentheses indicate this is a function pointer or
         reference type.  */
-      RETURN_IF_ERROR (result_append (dm, "()"));
+      RETURN_IF_ERROR (result_add (dm, "()"));
       /* Now demangle the function type.  The return type will be
         inserted before the `()', and the argument list will go after
         it.  */
@@ -2118,20 +2246,27 @@ demangle_type_ptr (dm, insert_pos, substitution_start)
         type.  Move it one character over so it points inside the
         `()'.  */
       ++(*insert_pos);
-    }
-  else
-    {
+      break;
+
+    case 'A':
+      /* An array pointer or reference.  demangle_array_type will figure
+        out where the asterisks and ampersands go.  */
+      RETURN_IF_ERROR (demangle_array_type (dm, insert_pos));
+      break;
+
+    default:
       /* No more pointer or reference tokens; this is therefore a
         pointer to data.  Finish up by demangling the underlying
         type.  */
       RETURN_IF_ERROR (demangle_type (dm));
       /* The pointer or reference characters follow the underlying
         type, as in `int*&'.  */
-      *insert_pos = result_length (dm);
+      *insert_pos = result_caret_pos (dm);
       /* Because of the production <type> ::= <substitution>,
         demangle_type will already have added the underlying type as
         a substitution candidate.  Don't do it again.  */
       is_substitution_candidate = 0;
+      break;
     }
   
   if (is_substitution_candidate)
@@ -2199,34 +2334,30 @@ demangle_type (dm)
        {
          status_t status;
          dyn_string_t cv_qualifiers = dyn_string_new (24);
+         int old_caret_position = result_get_caret (dm);
 
          if (cv_qualifiers == NULL)
            return STATUS_ALLOCATION_FAILED;
 
+         /* Decode all adjacent CV qualifiers.  */
          demangle_CV_qualifiers (dm, cv_qualifiers);
-
-         /* If the qualifiers apply to a pointer or reference, they
-            need to come after the whole qualified type.  */
-         if (peek_char (dm) == 'P' || peek_char (dm) == 'R')
-           {
-             status = demangle_type (dm);
-             if (STATUS_NO_ERROR (status))
-               status = result_append_space (dm);
-             if (STATUS_NO_ERROR (status))
-               status = result_append_string (dm, cv_qualifiers);
-           }
-         /* Otherwise, the qualifiers come first.  */
-         else
-           {
-             status = result_append_string (dm, cv_qualifiers);
-             if (STATUS_NO_ERROR (status))
-               status = result_append_space (dm);
-             if (STATUS_NO_ERROR (status))
-               status = demangle_type (dm);
-           }
-
+         /* Emit them, and shift the caret left so that the
+            underlying type will be emitted before the qualifiers.  */
+         status = result_add_string (dm, cv_qualifiers);
+         result_shift_caret (dm, -dyn_string_length (cv_qualifiers));
+         /* Clean up.  */
          dyn_string_delete (cv_qualifiers);
          RETURN_IF_ERROR (status);
+         /* Also prepend a blank, if needed.  */
+         RETURN_IF_ERROR (result_add_char (dm, ' '));
+         result_shift_caret (dm, -1);
+
+         /* Demangle the underlying type.  It will be emitted before
+            the CV qualifiers, since we moved the caret.  */
+         RETURN_IF_ERROR (demangle_type (dm));
+
+         /* Put the caret back where it was previously.  */
+         result_set_caret (dm, old_caret_position);
        }
        break;
 
@@ -2234,7 +2365,7 @@ demangle_type (dm)
        return "Non-pointer or -reference function type.";
 
       case 'A':
-       RETURN_IF_ERROR (demangle_array_type (dm));
+       RETURN_IF_ERROR (demangle_array_type (dm, NULL));
        break;
 
       case 'T':
@@ -2284,10 +2415,31 @@ demangle_type (dm)
              is_substitution_candidate = 0;
          }
        else
-         /* While the special substitution token itself is not a
-            substitution candidate, the <class-enum-type> is, so
-            don't clear is_substitution_candidate.  */
-         RETURN_IF_ERROR (demangle_class_enum_type (dm, &encode_return_type));
+         {
+           /* Now some trickiness.  We have a special substitution
+              here.  Often, the special substitution provides the
+              name of a template that's subsequently instantiated,
+              for instance `SaIcE' => std::allocator<char>.  In these
+              cases we need to add a substitution candidate for the
+              entire <class-enum-type> and thus don't want to clear
+              the is_substitution_candidate flag.
+
+              However, it's possible that what we have here is a
+              substitution token representing an entire type, such as
+              `Ss' => std::string.  In this case, we mustn't add a
+              new substitution candidate for this substitution token.
+              To detect this case, remember where the start of the
+              substitution token is.  */
+           const char *next = dm->next;
+           /* Now demangle the <class-enum-type>.  */
+           RETURN_IF_ERROR 
+             (demangle_class_enum_type (dm, &encode_return_type));
+           /* If all that was just demangled is the two-character
+              special substitution token, supress the addition of a
+              new candidate for it.  */
+           if (dm->next == next + 2)
+             is_substitution_candidate = 0;
+         }
 
        break;
 
@@ -2302,14 +2454,14 @@ demangle_type (dm)
 
       case 'C':
        /* A C99 complex type.  */
-       RETURN_IF_ERROR (result_append (dm, "complex "));
+       RETURN_IF_ERROR (result_add (dm, "complex "));
        advance_char (dm);
        RETURN_IF_ERROR (demangle_type (dm));
        break;
 
       case 'G':
        /* A C99 imaginary type.  */
-       RETURN_IF_ERROR (result_append (dm, "imaginary "));
+       RETURN_IF_ERROR (result_add (dm, "imaginary "));
        advance_char (dm);
        RETURN_IF_ERROR (demangle_type (dm));
        break;
@@ -2318,7 +2470,7 @@ demangle_type (dm)
        /* Vendor-extended type qualifier.  */
        advance_char (dm);
        RETURN_IF_ERROR (demangle_source_name (dm));
-       RETURN_IF_ERROR (result_append_char (dm, ' '));
+       RETURN_IF_ERROR (result_add_char (dm, ' '));
        RETURN_IF_ERROR (demangle_type (dm));
        break;
 
@@ -2372,6 +2524,39 @@ static const char *const builtin_type_names[26] =
   "..."                       /* z */
 };
 
+/* Java source names of builtin types.  Types that arn't valid in Java
+   are also included here - we don't fail if someone attempts to demangle a 
+   C++ symbol in Java style. */
+static const char *const java_builtin_type_names[26] = 
+{
+  "signed char",                /* a */
+  "boolean", /* C++ "bool" */   /* b */
+  "byte", /* C++ "char" */      /* c */
+  "double",                     /* d */
+  "long double",                /* e */
+  "float",                      /* f */
+  "__float128",                 /* g */
+  "unsigned char",              /* h */
+  "int",                        /* i */
+  "unsigned",                   /* j */
+  NULL,                         /* k */
+  "long",                       /* l */
+  "unsigned long",              /* m */
+  "__int128",                   /* n */
+  "unsigned __int128",          /* o */
+  NULL,                         /* p */
+  NULL,                         /* q */
+  NULL,                         /* r */
+  "short",                      /* s */
+  "unsigned short",             /* t */
+  NULL,                         /* u */
+  "void",                       /* v */
+  "char", /* C++ "wchar_t" */   /* w */
+  "long", /* C++ "long long" */ /* x */
+  "unsigned long long",         /* y */
+  "..."                         /* z */
+};
+
 /* Demangles and emits a <builtin-type>.  
 
     <builtin-type> ::= v  # void
@@ -2414,11 +2599,16 @@ demangle_builtin_type (dm)
     }
   else if (code >= 'a' && code <= 'z')
     {
-      const char *type_name = builtin_type_names[code - 'a'];
+      const char *type_name;
+      /* Java uses different names for some built-in types. */
+      if (dm->style == DMGL_JAVA)
+        type_name = java_builtin_type_names[code - 'a'];
+      else
+        type_name = builtin_type_names[code - 'a'];
       if (type_name == NULL)
        return "Unrecognized <builtin-type> code.";
 
-      RETURN_IF_ERROR (result_append (dm, type_name));
+      RETURN_IF_ERROR (result_add (dm, type_name));
       advance_char (dm);
       return STATUS_OK;
     }
@@ -2489,7 +2679,7 @@ demangle_function_type (dm, function_name_pos)
     {
       /* Indicate this function has C linkage if in verbose mode.  */
       if (flag_verbose)
-       RETURN_IF_ERROR (result_append (dm, " [extern \"C\"] "));
+       RETURN_IF_ERROR (result_add (dm, " [extern \"C\"] "));
       advance_char (dm);
     }
   RETURN_IF_ERROR (demangle_bare_function_type (dm, function_name_pos));
@@ -2516,7 +2706,7 @@ demangle_bare_function_type (dm, return_type_pos)
 
   DEMANGLE_TRACE ("bare-function-type", dm);
 
-  RETURN_IF_ERROR (result_append_char (dm, '('));
+  RETURN_IF_ERROR (result_add_char (dm, '('));
   while (!end_of_name_p (dm) && peek_char (dm) != 'E')
     {
       if (sequence == -1)
@@ -2558,7 +2748,7 @@ demangle_bare_function_type (dm, return_type_pos)
            {
              /* Separate parameter types by commas.  */
              if (sequence > 0)
-               RETURN_IF_ERROR (result_append (dm, ", "));
+               RETURN_IF_ERROR (result_add (dm, ", "));
              /* Demangle the type.  */
              RETURN_IF_ERROR (demangle_type (dm));
            }
@@ -2566,7 +2756,7 @@ demangle_bare_function_type (dm, return_type_pos)
 
       ++sequence;
     }
-  RETURN_IF_ERROR (result_append_char (dm, ')'));
+  RETURN_IF_ERROR (result_add_char (dm, ')'));
 
   /* We should have demangled at least one parameter type (which would
      be void, for a function that takes no parameters), plus the
@@ -2597,17 +2787,32 @@ demangle_class_enum_type (dm, encode_return_type)
 
 /* Demangles and emits an <array-type>.  
 
+   If PTR_INSERT_POS is not NULL, the array type is formatted as a
+   pointer or reference to an array, except that asterisk and
+   ampersand punctuation is omitted (since it's not know at this
+   point).  *PTR_INSERT_POS is set to the position in the demangled
+   name at which this punctuation should be inserted.  For example,
+   `A10_i' is demangled to `int () [10]' and *PTR_INSERT_POS points
+   between the parentheses.
+
+   If PTR_INSERT_POS is NULL, the array type is assumed not to be
+   pointer- or reference-qualified.  Then, for example, `A10_i' is
+   demangled simply as `int[10]'.  
+
     <array-type> ::= A [<dimension number>] _ <element type>  
                  ::= A <dimension expression> _ <element type>  */
 
 static status_t
-demangle_array_type (dm)
+demangle_array_type (dm, ptr_insert_pos)
      demangling_t dm;
+     int *ptr_insert_pos;
 {
   status_t status = STATUS_OK;
   dyn_string_t array_size = NULL;
   char peek;
 
+  DEMANGLE_TRACE ("array-type", dm);
+
   RETURN_IF_ERROR (demangle_char (dm, 'A'));
 
   /* Demangle the array size into array_size.  */
@@ -2641,13 +2846,24 @@ demangle_array_type (dm)
   if (STATUS_NO_ERROR (status))
     status = demangle_type (dm);
 
+  if (ptr_insert_pos != NULL)
+    {
+      /* This array is actually part of an pointer- or
+        reference-to-array type.  Format appropriately, except we
+        don't know which and how much punctuation to use.  */
+      if (STATUS_NO_ERROR (status))
+       status = result_add (dm, " () ");
+      /* Let the caller know where to insert the punctuation.  */
+      *ptr_insert_pos = result_caret_pos (dm) - 2;
+    }
+
   /* Emit the array dimension syntax.  */
   if (STATUS_NO_ERROR (status))
-    status = result_append_char (dm, '[');
+    status = result_add_char (dm, '[');
   if (STATUS_NO_ERROR (status) && array_size != NULL)
-    status = result_append_string (dm, array_size);
+    status = result_add_string (dm, array_size);
   if (STATUS_NO_ERROR (status))
-    status = result_append_char (dm, ']');
+    status = result_add_char (dm, ']');
   if (array_size != NULL)
     dyn_string_delete (array_size);
   
@@ -2691,7 +2907,7 @@ demangle_template_param (dm)
     /* parm_number exceeded the number of arguments in the current
        template argument list.  */
     return "Template parameter number out of bounds.";
-  RETURN_IF_ERROR (result_append_string (dm, (dyn_string_t) arg));
+  RETURN_IF_ERROR (result_add_string (dm, (dyn_string_t) arg));
 
   return STATUS_OK;
 }
@@ -2729,7 +2945,7 @@ demangle_template_args (dm)
       if (first)
        first = 0;
       else
-       RETURN_IF_ERROR (result_append (dm, ", "));
+       RETURN_IF_ERROR (result_add (dm, ", "));
 
       /* Capture the template arg.  */
       RETURN_IF_ERROR (result_push (dm));
@@ -2737,7 +2953,7 @@ demangle_template_args (dm)
       arg = result_pop (dm);
 
       /* Emit it in the demangled name.  */
-      RETURN_IF_ERROR (result_append_string (dm, (dyn_string_t) arg));
+      RETURN_IF_ERROR (result_add_string (dm, (dyn_string_t) arg));
 
       /* Save it for use in expanding <template-param>s.  */
       template_arg_list_add_arg (arg_list, arg);
@@ -2813,9 +3029,9 @@ demangle_literal (dm)
             corresponding to false or true, respectively.  */
          value = peek_char (dm);
          if (value == '0')
-           RETURN_IF_ERROR (result_append (dm, "false"));
+           RETURN_IF_ERROR (result_add (dm, "false"));
          else if (value == '1')
-           RETURN_IF_ERROR (result_append (dm, "true"));
+           RETURN_IF_ERROR (result_add (dm, "true"));
          else
            return "Unrecognized bool constant.";
          /* Consume the 0 or 1.  */
@@ -2833,10 +3049,10 @@ demangle_literal (dm)
          value_string = dyn_string_new (0);
          status = demangle_number_literally (dm, value_string, 10, 1);
          if (STATUS_NO_ERROR (status))
-           status = result_append_string (dm, value_string);
+           status = result_add_string (dm, value_string);
          /* For long integers, append an l.  */
          if (code == 'l' && STATUS_NO_ERROR (status))
-           status = result_append_char (dm, code);
+           status = result_add_char (dm, code);
          dyn_string_delete (value_string);
 
          RETURN_IF_ERROR (status);
@@ -2846,9 +3062,9 @@ demangle_literal (dm)
         literal's type explicitly using cast syntax.  */
     }
 
-  RETURN_IF_ERROR (result_append_char (dm, '('));
+  RETURN_IF_ERROR (result_add_char (dm, '('));
   RETURN_IF_ERROR (demangle_type (dm));
-  RETURN_IF_ERROR (result_append_char (dm, ')'));
+  RETURN_IF_ERROR (result_add_char (dm, ')'));
 
   value_string = dyn_string_new (0);
   if (value_string == NULL)
@@ -2856,7 +3072,7 @@ demangle_literal (dm)
 
   status = demangle_number_literally (dm, value_string, 10, 1);
   if (STATUS_NO_ERROR (status))
-    status = result_append_string (dm, value_string);
+    status = result_add_string (dm, value_string);
   dyn_string_delete (value_string);
   RETURN_IF_ERROR (status);
 
@@ -2944,30 +3160,30 @@ demangle_expression (dm)
       /* If it's binary, do an operand first.  */
       if (num_args > 1)
        {
-         status = result_append_char (dm, '(');
+         status = result_add_char (dm, '(');
          if (STATUS_NO_ERROR (status))
            status = demangle_expression (dm);
          if (STATUS_NO_ERROR (status))
-           status = result_append_char (dm, ')');
+           status = result_add_char (dm, ')');
        }
 
       /* Emit the operator.  */  
       if (STATUS_NO_ERROR (status))
-       status = result_append_string (dm, operator_name);
+       status = result_add_string (dm, operator_name);
       dyn_string_delete (operator_name);
       RETURN_IF_ERROR (status);
       
       /* Emit its second (if binary) or only (if unary) operand.  */
-      RETURN_IF_ERROR (result_append_char (dm, '('));
+      RETURN_IF_ERROR (result_add_char (dm, '('));
       RETURN_IF_ERROR (demangle_expression (dm));
-      RETURN_IF_ERROR (result_append_char (dm, ')'));
+      RETURN_IF_ERROR (result_add_char (dm, ')'));
 
       /* The ternary operator takes a third operand.  */
       if (num_args == 3)
        {
-         RETURN_IF_ERROR (result_append (dm, ":("));
+         RETURN_IF_ERROR (result_add (dm, ":("));
          RETURN_IF_ERROR (demangle_expression (dm));
-         RETURN_IF_ERROR (result_append_char (dm, ')'));
+         RETURN_IF_ERROR (result_add_char (dm, ')'));
        }
     }
 
@@ -2986,7 +3202,7 @@ demangle_scope_expression (dm)
   RETURN_IF_ERROR (demangle_char (dm, 's'));
   RETURN_IF_ERROR (demangle_char (dm, 'r'));
   RETURN_IF_ERROR (demangle_type (dm));
-  RETURN_IF_ERROR (result_append (dm, "::"));
+  RETURN_IF_ERROR (result_add (dm, "::"));
   RETURN_IF_ERROR (demangle_encoding (dm));
   return STATUS_OK;
 }
@@ -3077,17 +3293,17 @@ demangle_substitution (dm, template_p)
       switch (peek)
        {
        case 't':
-         RETURN_IF_ERROR (result_append (dm, "std"));
+         RETURN_IF_ERROR (result_add (dm, "std"));
          break;
 
        case 'a':
-         RETURN_IF_ERROR (result_append (dm, "std::allocator"));
+         RETURN_IF_ERROR (result_add (dm, "std::allocator"));
          new_last_source_name = "allocator";
          *template_p = 1;
          break;
 
        case 'b':
-         RETURN_IF_ERROR (result_append (dm, "std::basic_string"));
+         RETURN_IF_ERROR (result_add (dm, "std::basic_string"));
          new_last_source_name = "basic_string";
          *template_p = 1;
          break;
@@ -3095,12 +3311,12 @@ demangle_substitution (dm, template_p)
        case 's':
          if (!flag_verbose)
            {
-             RETURN_IF_ERROR (result_append (dm, "std::string"));
+             RETURN_IF_ERROR (result_add (dm, "std::string"));
              new_last_source_name = "string";
            }
          else
            {
-             RETURN_IF_ERROR (result_append (dm, "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"));
+             RETURN_IF_ERROR (result_add (dm, "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"));
              new_last_source_name = "basic_string";
            }
          *template_p = 0;
@@ -3109,12 +3325,12 @@ demangle_substitution (dm, template_p)
        case 'i':
          if (!flag_verbose)
            {
-             RETURN_IF_ERROR (result_append (dm, "std::istream"));
+             RETURN_IF_ERROR (result_add (dm, "std::istream"));
              new_last_source_name = "istream";
            }
          else
            {
-             RETURN_IF_ERROR (result_append (dm, "std::basic_istream<char, std::char_traints<char> >"));
+             RETURN_IF_ERROR (result_add (dm, "std::basic_istream<char, std::char_traints<char> >"));
              new_last_source_name = "basic_istream";
            }
          *template_p = 0;
@@ -3123,12 +3339,12 @@ demangle_substitution (dm, template_p)
        case 'o':
          if (!flag_verbose)
            {
-             RETURN_IF_ERROR (result_append (dm, "std::ostream"));
+             RETURN_IF_ERROR (result_add (dm, "std::ostream"));
              new_last_source_name = "ostream";
            }
          else
            {
-             RETURN_IF_ERROR (result_append (dm, "std::basic_ostream<char, std::char_traits<char> >"));
+             RETURN_IF_ERROR (result_add (dm, "std::basic_ostream<char, std::char_traits<char> >"));
              new_last_source_name = "basic_ostream";
            }
          *template_p = 0;
@@ -3137,12 +3353,12 @@ demangle_substitution (dm, template_p)
        case 'd':
          if (!flag_verbose) 
            {
-             RETURN_IF_ERROR (result_append (dm, "std::iostream"));
+             RETURN_IF_ERROR (result_add (dm, "std::iostream"));
              new_last_source_name = "iostream";
            }
          else
            {
-             RETURN_IF_ERROR (result_append (dm, "std::basic_iostream<char, std::char_traits<char> >"));
+             RETURN_IF_ERROR (result_add (dm, "std::basic_iostream<char, std::char_traits<char> >"));
              new_last_source_name = "basic_iostream";
            }
          *template_p = 0;
@@ -3173,7 +3389,7 @@ demangle_substitution (dm, template_p)
     return "Substitution number out of range.";
 
   /* Emit the substitution text.  */
-  RETURN_IF_ERROR (result_append_string (dm, text));
+  RETURN_IF_ERROR (result_add_string (dm, text));
 
   RETURN_IF_ERROR (demangle_char (dm, '_'));
   return STATUS_OK;
@@ -3193,12 +3409,12 @@ demangle_local_name (dm)
   RETURN_IF_ERROR (demangle_char (dm, 'Z'));
   RETURN_IF_ERROR (demangle_encoding (dm));
   RETURN_IF_ERROR (demangle_char (dm, 'E'));
-  RETURN_IF_ERROR (result_append (dm, "::"));
+  RETURN_IF_ERROR (result_add (dm, "::"));
 
   if (peek_char (dm) == 's')
     {
       /* Local character string literal.  */
-      RETURN_IF_ERROR (result_append (dm, "string literal"));
+      RETURN_IF_ERROR (result_add (dm, "string literal"));
       /* Consume the s.  */
       advance_char (dm);
       RETURN_IF_ERROR (demangle_discriminator (dm, 0));
@@ -3235,7 +3451,7 @@ demangle_discriminator (dm, suppress_first)
       /* Consume the underscore.  */
       advance_char (dm);
       if (flag_verbose)
-       RETURN_IF_ERROR (result_append (dm, " [#"));
+       RETURN_IF_ERROR (result_add (dm, " [#"));
       /* Check if there's a number following the underscore.  */
       if (IS_DIGIT ((unsigned char) peek_char (dm)))
        {
@@ -3246,22 +3462,18 @@ demangle_discriminator (dm, suppress_first)
            /* Write the discriminator.  The mangled number is two
               less than the discriminator ordinal, counting from
               zero.  */
-           RETURN_IF_ERROR (int_to_dyn_string (discriminator + 2, 
+           RETURN_IF_ERROR (int_to_dyn_string (discriminator + 1,
                                                (dyn_string_t) dm->result));
        }
       else
-       {
-         if (flag_verbose)
-           /* A missing digit correspond to one.  */
-           RETURN_IF_ERROR (result_append_char (dm, '1'));
-       }
+       return STATUS_ERROR;
       if (flag_verbose)
-       RETURN_IF_ERROR (result_append_char (dm, ']'));
+       RETURN_IF_ERROR (result_add_char (dm, ']'));
     }
   else if (!suppress_first)
     {
       if (flag_verbose)
-       RETURN_IF_ERROR (result_append (dm, " [#0]"));
+       RETURN_IF_ERROR (result_add (dm, " [#0]"));
     }
 
   return STATUS_OK;
@@ -3272,16 +3484,17 @@ demangle_discriminator (dm, suppress_first)
    an error message, and the contents of RESULT are unchanged.  */
 
 static status_t
-cp_demangle (name, result)
+cp_demangle (name, result, style)
      const char *name;
      dyn_string_t result;
+     int style;
 {
   status_t status;
   int length = strlen (name);
 
   if (length > 2 && name[0] == '_' && name[1] == 'Z')
     {
-      demangling_t dm = demangling_new (name);
+      demangling_t dm = demangling_new (name, style);
       if (dm == NULL)
        return STATUS_ALLOCATION_FAILED;
 
@@ -3327,7 +3540,7 @@ cp_demangle_type (type_name, result)
      dyn_string_t result;
 {
   status_t status;
-  demangling_t dm = demangling_new (type_name);
+  demangling_t dm = demangling_new (type_name, DMGL_GNU_V3);
   
   if (dm == NULL)
     return STATUS_ALLOCATION_FAILED;
@@ -3360,7 +3573,7 @@ cp_demangle_type (type_name, result)
 
 extern char *__cxa_demangle PARAMS ((const char *, char *, size_t *, int *));
 
-/* ABI-mandated entry point in the C++ runtime library for performing
+/* ia64 ABI-mandated entry point in the C++ runtime library for performing
    demangling.  MANGLED_NAME is a NUL-terminated character string
    containing the name to be demangled.  
 
@@ -3428,7 +3641,7 @@ __cxa_demangle (mangled_name, output_buffer, length, status)
   if (mangled_name[0] == '_' && mangled_name[1] == 'Z')
     /* MANGLED_NAME apprears to be a function or variable name.
        Demangle it accordingly.  */
-    result = cp_demangle (mangled_name, &demangled_name);
+    result = cp_demangle (mangled_name, &demangled_name, 0);
   else
     /* Try to demangled MANGLED_NAME as the name of a type.  */
     result = cp_demangle_type (mangled_name, &demangled_name);
@@ -3474,13 +3687,21 @@ __cxa_demangle (mangled_name, output_buffer, length, status)
    If the demangling failes, returns NULL.  */
 
 char *
-cplus_demangle_new_abi (mangled)
+cplus_demangle_v3 (mangled)
      const char* mangled;
 {
+  dyn_string_t demangled;
+  status_t status;
+
+  /* If this isn't a mangled name, don't pretend to demangle it.  */
+  if (strncmp (mangled, "_Z", 2) != 0)
+    return NULL;
+
   /* Create a dyn_string to hold the demangled name.  */
-  dyn_string_t demangled = dyn_string_new (0);
+  demangled = dyn_string_new (0);
   /* Attempt the demangling.  */
-  status_t status = cp_demangle ((char *) mangled, demangled);
+  status = cp_demangle ((char *) mangled, demangled, 0);
+
   if (STATUS_NO_ERROR (status))
     /* Demangling succeeded.  */
     {
@@ -3503,8 +3724,194 @@ cplus_demangle_new_abi (mangled)
     }
 }
 
+/* Demangle a Java symbol.  Java uses a subset of the V3 ABI C++ mangling 
+   conventions, but the output formatting is a little different.
+   This instructs the C++ demangler not to emit pointer characters ("*"), and 
+   to use Java's namespace separator symbol ("." instead of "::").  It then 
+   does an additional pass over the demangled output to replace instances 
+   of JArray<TYPE> with TYPE[].  */
+
+char *
+java_demangle_v3 (mangled)
+     const char* mangled;
+{
+  dyn_string_t demangled;
+  char *next;
+  char *end;
+  int len;
+  status_t status;
+  int nesting = 0;
+  char *cplus_demangled;
+  char *return_value;
+    
+  /* Create a dyn_string to hold the demangled name.  */
+  demangled = dyn_string_new (0);
+
+  /* Attempt the demangling.  */
+  status = cp_demangle ((char *) mangled, demangled, DMGL_JAVA);
+
+  if (STATUS_NO_ERROR (status))
+    /* Demangling succeeded.  */
+    {
+      /* Grab the demangled result from the dyn_string. */
+      cplus_demangled = dyn_string_release (demangled);
+    }
+  else if (status == STATUS_ALLOCATION_FAILED)
+    {
+      fprintf (stderr, "Memory allocation failed.\n");
+      abort ();
+    }
+  else
+    /* Demangling failed.  */
+    {
+      dyn_string_delete (demangled);
+      return NULL;
+    }
+  
+  len = strlen (cplus_demangled);
+  next = cplus_demangled;
+  end = next + len;
+  demangled = NULL;
+
+  /* Replace occurances of JArray<TYPE> with TYPE[]. */
+  while (next < end)
+    {
+      char *open_str = strstr (next, "JArray<");
+      char *close_str = NULL;
+      if (nesting > 0)
+       close_str = strchr (next, '>');
+    
+      if (open_str != NULL && (close_str == NULL || close_str > open_str))
+        {
+         ++nesting;
+         
+         if (!demangled)
+           demangled = dyn_string_new(len);
+
+          /* Copy prepending symbols, if any. */
+         if (open_str > next)
+           {
+             open_str[0] = 0;
+             dyn_string_append_cstr (demangled, next);
+           }     
+         next = open_str + 7;
+       }
+      else if (close_str != NULL)
+        {
+         --nesting;
+         
+          /* Copy prepending type symbol, if any. Squash any spurious 
+            whitespace. */
+         if (close_str > next && next[0] != ' ')
+           {
+             close_str[0] = 0;
+             dyn_string_append_cstr (demangled, next);
+           }
+         dyn_string_append_cstr (demangled, "[]");       
+         next = close_str + 1;
+       }
+      else
+        {
+         /* There are no more arrays. Copy the rest of the symbol, or
+            simply return the original symbol if no changes were made. */
+         if (next == cplus_demangled)
+           return cplus_demangled;
+
+          dyn_string_append_cstr (demangled, next);
+         next = end;
+       }
+    }
+
+  free (cplus_demangled);
+  
+  return_value = dyn_string_release (demangled);
+  return return_value;
+}
+
 #endif /* IN_LIBGCC2 */
 
+
+/* Demangle NAME in the G++ V3 ABI demangling style, and return either
+   zero, indicating that some error occurred, or a demangling_t
+   holding the results.  */
+static demangling_t
+demangle_v3_with_details (name)
+     const char *name;
+{
+  demangling_t dm;
+  status_t status;
+
+  if (strncmp (name, "_Z", 2))
+    return 0;
+
+  dm = demangling_new (name, DMGL_GNU_V3);
+  if (dm == NULL)
+    {
+      fprintf (stderr, "Memory allocation failed.\n");
+      abort ();
+    }
+
+  status = result_push (dm);
+  if (! STATUS_NO_ERROR (status))
+    {
+      demangling_delete (dm);
+      fprintf (stderr, "%s\n", status);
+      abort ();
+    }
+
+  status = demangle_mangled_name (dm);
+  if (STATUS_NO_ERROR (status))
+    return dm;
+
+  demangling_delete (dm);
+  return 0;
+}
+
+
+/* Return non-zero iff NAME is the mangled form of a constructor name
+   in the G++ V3 ABI demangling style.  Specifically, return:
+   - '1' if NAME is a complete object constructor,
+   - '2' if NAME is a base object constructor, or
+   - '3' if NAME is a complete object allocating constructor.  */
+enum gnu_v3_ctor_kinds
+is_gnu_v3_mangled_ctor (name)
+     const char *name;
+{
+  demangling_t dm = demangle_v3_with_details (name);
+
+  if (dm)
+    {
+      enum gnu_v3_ctor_kinds result = dm->is_constructor;
+      demangling_delete (dm);
+      return result;
+    }
+  else
+    return 0;
+}
+
+
+/* Return non-zero iff NAME is the mangled form of a destructor name
+   in the G++ V3 ABI demangling style.  Specifically, return:
+   - '0' if NAME is a deleting destructor,
+   - '1' if NAME is a complete object destructor, or
+   - '2' if NAME is a base object destructor.  */
+enum gnu_v3_dtor_kinds
+is_gnu_v3_mangled_dtor (name)
+     const char *name;
+{
+  demangling_t dm = demangle_v3_with_details (name);
+
+  if (dm)
+    {
+      enum gnu_v3_dtor_kinds result = dm->is_destructor;
+      demangling_delete (dm);
+      return result;
+    }
+  else
+    return 0;
+}
+
+
 #ifdef STANDALONE_DEMANGLER
 
 #include "getopt.h"
@@ -3538,7 +3945,7 @@ print_usage (fp, exit_value)
 }
 
 /* Option specification for getopt_long.  */
-static struct option long_options[] = 
+static const struct option long_options[] = 
 {
   { "help",    no_argument, NULL, 'h' },
   { "strict",  no_argument, NULL, 's' },
@@ -3640,7 +4047,7 @@ main (argc, argv)
            }
 
          /* Attempt to demangle the name.  */
-         status = cp_demangle (dyn_string_buf (mangled), demangled);
+         status = cp_demangle (dyn_string_buf (mangled), demangled, 0);
 
          /* If the demangling succeeded, great!  Print out the
             demangled version.  */
@@ -3679,7 +4086,7 @@ main (argc, argv)
       for (i = optind; i < argc; ++i)
        {
          /* Attempt to demangle.  */
-         status = cp_demangle (argv[i], result);
+         status = cp_demangle (argv[i], result, 0);
 
          /* If it worked, print the demangled name.  */
          if (STATUS_NO_ERROR (status))
@@ -3687,7 +4094,7 @@ main (argc, argv)
          /* Abort on allocaiton failures.  */
          else if (status == STATUS_ALLOCATION_FAILED)
            {
-             fprintf (stderr, "Memory allocaiton failed.\n");
+             fprintf (stderr, "Memory allocation failed.\n");
              abort ();
            }
          /* If not, print the error message to stderr instead.  */