OSDN Git Service

2002-02-01 H.J. Lu <hjl@gnu.org>
[pf3gnuchains/gcc-fork.git] / libiberty / cp-demangle.c
index 651f7aa..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
 */
 
 /* 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.  */
+   executable (functionally similar to c++filt, but includes this
+   demangler only).  */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
+#include <sys/types.h>
+
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
   (((CHAR) >= 'a' && (CHAR) <= 'z')                                     \
    || ((CHAR) >= 'A' && (CHAR) <= 'Z'))
 
+/* The prefix prepended by GCC to an identifier represnting the
+   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.
@@ -70,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;
 };
 
@@ -89,17 +110,10 @@ struct substitution_def
   /* The demangled text of the substitution.  */
   dyn_string_t text;
 
-  /* The template parameter that this represents, indexed from zero.
-     If this is not a template paramter number, the value is
-     NOT_TEMPLATE_PARM.  */
-  int template_parm_number;
-
   /* Whether this substitution represents a template item.  */
   int template_p : 1;
 };
 
-#define NOT_TEMPLATE_PARM (-1)
-
 /* Data structure representing a template argument list.  */
 
 struct template_arg_list_def
@@ -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.  */
@@ -194,8 +220,8 @@ static string_list_t string_list_new
   PARAMS ((int));
 static void string_list_delete
   PARAMS ((string_list_t));
-static status_t result_close_template_list 
-  PARAMS ((demangling_t));
+static status_t result_add_separated_char
+  PARAMS ((demangling_t, int));
 static status_t result_push
   PARAMS ((demangling_t));
 static string_list_t result_pop
@@ -203,7 +229,7 @@ static string_list_t result_pop
 static int substitution_start
   PARAMS ((demangling_t));
 static status_t substitution_add
-  PARAMS ((demangling_t, int, int, int));
+  PARAMS ((demangling_t, int, int));
 static dyn_string_t substitution_get
   PARAMS ((demangling_t, int, int *));
 #ifdef CP_DEMANGLE_DEBUG
@@ -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,30 +296,56 @@ 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.  */
+#define result_insert_string(DM, POS, STRING)                           \
+  (dyn_string_insert (&(DM)->result->string, (POS), (STRING))           \
+   ? STATUS_OK : STATUS_ALLOCATION_FAILED)
+
+/* Inserts NUL-terminated string CSTR to the demangled result at
+   position POS.  */
+#define result_insert(DM, POS, CSTR)                                    \
+  (dyn_string_insert_cstr (&(DM)->result->string, (POS), (CSTR))        \
+   ? STATUS_OK : STATUS_ALLOCATION_FAILED)
+
+/* Inserts character CHAR to the demangled result at position POS.  */
+#define result_insert_char(DM, POS, CHAR)                               \
+  (dyn_string_insert_char (&(DM)->result->string, (POS), (CHAR))        \
    ? STATUS_OK : STATUS_ALLOCATION_FAILED)
 
 /* The length of the current demangled result.  */
 #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
+   brackets with the previous character.  */
+#define result_open_template_list(DM) result_add_separated_char(DM, '<')
+#define result_close_template_list(DM) result_add_separated_char(DM, '>')
 
 /* Appends a base 10 representation of VALUE to DS.  STATUS_OK on
    success.  On failure, deletes DS and returns an error code.  */
@@ -355,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))
@@ -371,34 +424,28 @@ string_list_delete (node)
   while (node != NULL)
     {
       string_list_t next = node->next;
-      free (node);
+      dyn_string_delete ((dyn_string_t) node);
       node = next;
     }
 }
 
-/* Appends a greater-than character to the demangled result.  If the
-   last character is a greater-than character, a space is inserted
-   first, so that the two greater-than characters don't look like a
-   right shift token.  */
+/* Appends CHARACTER to the demangled result.  If the current trailing
+   character of the result is CHARACTER, a space is inserted first.  */
 
 static status_t
-result_close_template_list (dm)
+result_add_separated_char (dm, character)
      demangling_t dm;
+     int character;
 {
-  dyn_string_t s = &dm->result->string;
+  char *result = dyn_string_buf (result_string (dm));
+  int caret_pos = result_caret_pos (dm);
 
-  /* 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) == '>')
-    {
-      if (!dyn_string_append_char (s, ' '))
-       return STATUS_ALLOCATION_FAILED;
-    }
-
-  /* Add closing angle brackets.  */
-  if (!dyn_string_append_char (s, '>'))
-    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;
 }
@@ -436,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.  */
@@ -444,24 +536,18 @@ 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
    START_POSITION as a potential substitution.  If TEMPLATE_P is
-   non-zero, this potential substitution is a template-id.  
-
-   If TEMPLATE_PARM_NUMBER is not NOT_TEMPLATE_PARM, the substitution
-   is for that particular <template-param>, and is distinct from other
-   otherwise-identical types and other <template-param>s with
-   different indices.  */
+   non-zero, this potential substitution is a template-id.  */
 
 static status_t
-substitution_add (dm, start_position, template_p, template_parm_number)
+substitution_add (dm, start_position, template_p)
      demangling_t dm;
      int start_position;
      int template_p;
-     int template_parm_number;
 {
   dyn_string_t result = result_string (dm);
   dyn_string_t substitution = dyn_string_new (0);
@@ -473,29 +559,20 @@ substitution_add (dm, start_position, template_p, template_parm_number)
   /* 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;
     }
 
-  /* Check whether SUBSTITUTION already occurs.  */
-  for (i = 0; i < dm->num_substitutions; ++i)
-    if (dyn_string_eq (dm->substitutions[i].text, substitution)
-       && dm->substitutions[i].template_parm_number == template_parm_number)
-      /* Found SUBSTITUTION already present.  */
-      {
-       /* Callers expect this function to take ownership of
-          SUBSTITUTION, so delete it.  */
-       dyn_string_delete (substitution);
-       return STATUS_OK;
-      }
-
   /* If there's no room for the new entry, grow the array.  */
   if (dm->substitutions_allocated == dm->num_substitutions)
     {
       size_t new_array_size;
-      dm->substitutions_allocated *= 2;
+      if (dm->substitutions_allocated > 0)
+       dm->substitutions_allocated *= 2;
+      else
+       dm->substitutions_allocated = 2;
       new_array_size = 
        sizeof (struct substitution_def) * dm->substitutions_allocated;
 
@@ -510,10 +587,9 @@ substitution_add (dm, start_position, template_p, template_parm_number)
     }
 
   /* Add the substitution to the array.  */
+  i = dm->num_substitutions++;
   dm->substitutions[i].text = substitution;
   dm->substitutions[i].template_p = template_p;
-  dm->substitutions[i].template_parm_number = template_parm_number;
-  ++dm->num_substitutions;
 
 #ifdef CP_DEMANGLE_DEBUG
   substitutions_print (dm, stderr);
@@ -722,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));
@@ -746,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;
 }
@@ -799,7 +879,7 @@ static status_t demangle_nested_name
 static status_t demangle_prefix
   PARAMS ((demangling_t, int *));
 static status_t demangle_unqualified_name
-  PARAMS ((demangling_t));
+  PARAMS ((demangling_t, int *));
 static status_t demangle_source_name
   PARAMS ((demangling_t));
 static status_t demangle_number
@@ -810,12 +890,18 @@ static status_t demangle_identifier
   PARAMS ((demangling_t, int, dyn_string_t));
 static status_t demangle_operator_name
   PARAMS ((demangling_t, int, int *));
+static status_t demangle_nv_offset
+  PARAMS ((demangling_t));
+static status_t demangle_v_offset
+  PARAMS ((demangling_t));
+static status_t demangle_call_offset
+  PARAMS ((demangling_t));
 static status_t demangle_special_name
   PARAMS ((demangling_t));
 static status_t demangle_ctor_dtor_name
   PARAMS ((demangling_t));
 static status_t demangle_type_ptr
-  PARAMS ((demangling_t));
+  PARAMS ((demangling_t, int *, int));
 static status_t demangle_type
   PARAMS ((demangling_t));
 static status_t demangle_CV_qualifiers
@@ -823,15 +909,15 @@ static status_t demangle_CV_qualifiers
 static status_t demangle_builtin_type
   PARAMS ((demangling_t));
 static status_t demangle_function_type
-  PARAMS ((demangling_t, int));
+  PARAMS ((demangling_t, int *));
 static status_t demangle_bare_function_type
-  PARAMS ((demangling_t, int));
+  PARAMS ((demangling_t, int *));
 static status_t demangle_class_enum_type
   PARAMS ((demangling_t, int *));
 static status_t demangle_array_type
-  PARAMS ((demangling_t));
-static status_t demangle_template_param
   PARAMS ((demangling_t, int *));
+static status_t demangle_template_param
+  PARAMS ((demangling_t));
 static status_t demangle_template_args
   PARAMS ((demangling_t));
 static status_t demangle_literal
@@ -845,19 +931,21 @@ static status_t demangle_scope_expression
 static status_t demangle_expr_primary
   PARAMS ((demangling_t));
 static status_t demangle_substitution
-  PARAMS ((demangling_t, int *, int *));
+  PARAMS ((demangling_t, int *));
 static status_t demangle_local_name
   PARAMS ((demangling_t));
 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));
+#endif
 
 /* When passed to demangle_bare_function_type, indicates that the
    function's return type is not encoded before its parameter types.  */
-#define BFT_NO_RETURN_TYPE    -1
+#define BFT_NO_RETURN_TYPE    NULL
 
 /* Check that the next character is C.  If so, consume it.  If not,
    return an error.  */
@@ -902,16 +990,14 @@ demangle_mangled_name (dm)
 
     <encoding>         ::= <function name> <bare-function-type>
                        ::= <data name>
-                       ::= <substitution>  */
+                       ::= <special-name>  */
 
 static status_t
 demangle_encoding (dm)
      demangling_t dm;
 {
-  int template_p;
-  int special_std_substitution;
+  int encode_return_type;
   int start_position;
-  int start = substitution_start (dm);
   template_arg_list_t old_arg_list = current_template_arg_list (dm);
   char peek = peek_char (dm);
 
@@ -919,45 +1005,31 @@ 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 == 'S')
-    {
-      RETURN_IF_ERROR (demangle_substitution (dm, &template_p,
-                                             &special_std_substitution));
-      if (special_std_substitution)
-       {
-         /* This was the magic `std::' substitution.  */
-         RETURN_IF_ERROR (result_append (dm, "::"));
-         RETURN_IF_ERROR (demangle_encoding (dm));
-       }
-    }
-  else if (peek == 'G' || peek == 'T')
+  if (peek == 'G' || peek == 'T')
     RETURN_IF_ERROR (demangle_special_name (dm));
   else
     {
       /* Now demangle the name.  */
-      RETURN_IF_ERROR (demangle_name (dm, &template_p));
+      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')
        {
-         if (template_p)
+         if (encode_return_type)
            /* Template functions have their return type encoded.  The
               return type should be inserted at start_position.  */
            RETURN_IF_ERROR 
-             (demangle_bare_function_type (dm, start_position));
+             (demangle_bare_function_type (dm, &start_position));
          else
            /* Non-template functions don't have their return type
               encoded.  */
            RETURN_IF_ERROR 
              (demangle_bare_function_type (dm, BFT_NO_RETURN_TYPE)); 
        }
-
-      RETURN_IF_ERROR (substitution_add (dm, start, template_p, 
-                                        NOT_TEMPLATE_PARM));
     }
 
   /* Pop off template argument lists that were built during the
@@ -982,69 +1054,81 @@ demangle_encoding (dm)
                         ::= <substitution>  */
 
 static status_t
-demangle_name (dm, template_p)
+demangle_name (dm, encode_return_type)
      demangling_t dm;
-     int *template_p;
+     int *encode_return_type;
 {
-  int special_std_substitution;
   int start = substitution_start (dm);
+  char peek = peek_char (dm);
+  int is_std_substitution = 0;
+
+  /* Generally, the return type is encoded if the function is a
+     template-id, and suppressed otherwise.  There are a few cases,
+     though, in which the return type is not encoded even for a
+     templated function.  In these cases, this flag is set.  */
+  int suppress_return_type = 0;
 
   DEMANGLE_TRACE ("name", dm);
 
-  switch (peek_char (dm))
+  switch (peek)
     {
     case 'N':
       /* This is a <nested-name>.  */
-      RETURN_IF_ERROR (demangle_nested_name (dm, template_p));
+      RETURN_IF_ERROR (demangle_nested_name (dm, encode_return_type));
       break;
 
     case 'Z':
       RETURN_IF_ERROR (demangle_local_name (dm));
+      *encode_return_type = 0;
       break;
 
     case 'S':
       /* The `St' substitution allows a name nested in std:: to appear
-        without being enclosed in a nested name.
-          <name> ::= St <unqualified-name>     # ::std::  */
+        without being enclosed in a nested name.  */
       if (peek_char_next (dm) == 't') 
        {
          (void) next_char (dm);
          (void) next_char (dm);
-         RETURN_IF_ERROR (result_append (dm, "std::"));
-         RETURN_IF_ERROR (demangle_unqualified_name (dm));
+         RETURN_IF_ERROR (result_add (dm, "std::"));
+         RETURN_IF_ERROR 
+           (demangle_unqualified_name (dm, &suppress_return_type));
+         is_std_substitution = 1;
        }
       else
+       RETURN_IF_ERROR (demangle_substitution (dm, encode_return_type));
+      /* Check if a template argument list immediately follows.
+        If so, then we just demangled an <unqualified-template-name>.  */
+      if (peek_char (dm) == 'I') 
        {
-         RETURN_IF_ERROR (demangle_substitution (dm, template_p,
-                                                 &special_std_substitution));
-         if (special_std_substitution)
-           {
-             /* This was the magic `std::' substitution.  We can have
-                a <nested-name> or one of the unscoped names
-                following.  */
-             RETURN_IF_ERROR (result_append (dm, "::"));
-             RETURN_IF_ERROR (demangle_name (dm, template_p));
-           }
+         /* A template name of the form std::<unqualified-name> is a
+             substitution candidate.  */
+         if (is_std_substitution)
+           RETURN_IF_ERROR (substitution_add (dm, start, 0));
+         /* Demangle the <template-args> here.  */
+         RETURN_IF_ERROR (demangle_template_args (dm));
+         *encode_return_type = !suppress_return_type;
        }
+      else
+       *encode_return_type = 0;
+
       break;
 
     default:
       /* This is an <unscoped-name> or <unscoped-template-name>.  */
-      RETURN_IF_ERROR (demangle_unqualified_name (dm));
+      RETURN_IF_ERROR (demangle_unqualified_name (dm, &suppress_return_type));
 
       /* If the <unqualified-name> is followed by template args, this
         is an <unscoped-template-name>.  */
       if (peek_char (dm) == 'I')
        {
          /* Add a substitution for the unqualified template name.  */
-         RETURN_IF_ERROR (substitution_add (dm, start, 0, 
-                                            NOT_TEMPLATE_PARM));
+         RETURN_IF_ERROR (substitution_add (dm, start, 0));
 
          RETURN_IF_ERROR (demangle_template_args (dm));
-         *template_p = 1;
+         *encode_return_type = !suppress_return_type;
        }
       else
-       *template_p = 0;
+       *encode_return_type = 0;
 
       break;
     }
@@ -1054,12 +1138,12 @@ demangle_name (dm, template_p)
 
 /* Demangles and emits a <nested-name>. 
 
-    <nested-name>       ::= N [<CV-qualifiers>] <prefix> <component> E  */
+    <nested-name>     ::= N [<CV-qualifiers>] <prefix> <unqulified-name> E  */
 
 static status_t
-demangle_nested_name (dm, template_p)
+demangle_nested_name (dm, encode_return_type)
      demangling_t dm;
-     int *template_p;
+     int *encode_return_type;
 {
   char peek;
 
@@ -1070,23 +1154,33 @@ demangle_nested_name (dm, template_p)
   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, template_p));
-  /* No need to demangle the final <component>; demangle_prefix will
-     handle it.  */
+
+  RETURN_IF_ERROR (demangle_prefix (dm, encode_return_type));
+  /* No need to demangle the final <unqualified-name>; demangle_prefix
+     will handle it.  */
   RETURN_IF_ERROR (demangle_char (dm, 'E'));
 
   return STATUS_OK;
@@ -1094,41 +1188,52 @@ demangle_nested_name (dm, template_p)
 
 /* Demangles and emits a <prefix>.
 
-    <prefix>            ::= <prefix> <component>
+    <prefix>            ::= <prefix> <unqualified-name>
                         ::= <template-prefix> <template-args>
                        ::= # empty
                        ::= <substitution>
 
     <template-prefix>   ::= <prefix>
-                        ::= <substitution>
-
-    <component>         ::= <unqualified-name>
-                        ::= <local-name>  */
+                        ::= <substitution>  */
 
 static status_t
-demangle_prefix (dm, template_p)
+demangle_prefix (dm, encode_return_type)
      demangling_t dm;
-     int *template_p;
+     int *encode_return_type;
 {
   int start = substitution_start (dm);
   int nested = 0;
 
-  /* TEMPLATE_P is updated as we decend the nesting chain.  After
-     <template-args>, it is set to non-zero; after everything else it
-     is set to zero.  */
+  /* ENCODE_RETURN_TYPE is updated as we decend the nesting chain.
+     After <template-args>, it is set to non-zero; after everything
+     else it is set to zero.  */
+
+  /* Generally, the return type is encoded if the function is a
+     template-id, and suppressed otherwise.  There are a few cases,
+     though, in which the return type is not encoded even for a
+     templated function.  In these cases, this flag is set.  */
+  int suppress_return_type = 0;
 
   DEMANGLE_TRACE ("prefix", dm);
 
   while (1)
     {
       char peek;
-      int unused;
 
       if (end_of_name_p (dm))
        return "Unexpected end of name in <compound-name>.";
 
       peek = peek_char (dm);
       
+      /* We'll initialize suppress_return_type to false, and set it to true
+        if we end up demangling a constructor name.  However, make
+        sure we're not actually about to demangle template arguments
+        -- if so, this is the <template-args> following a
+        <template-prefix>, so we'll want the previous flag value
+        around.  */
+      if (peek != 'I')
+       suppress_return_type = 0;
+
       if (IS_DIGIT ((unsigned char) peek)
          || (peek >= 'a' && peek <= 'z')
          || peek == 'C' || peek == 'D'
@@ -1136,31 +1241,36 @@ demangle_prefix (dm, template_p)
        {
          /* 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;
 
          if (peek == 'S')
            /* The substitution determines whether this is a
-              template-id.   */
-           RETURN_IF_ERROR (demangle_substitution (dm, template_p, 
-                                                   &unused));
+              template-id.  */
+           RETURN_IF_ERROR (demangle_substitution (dm, encode_return_type));
          else
            {
-             RETURN_IF_ERROR (demangle_unqualified_name (dm));
-             *template_p = 0;
+             /* It's just a name.  */
+             RETURN_IF_ERROR 
+               (demangle_unqualified_name (dm, &suppress_return_type));
+             *encode_return_type = 0;
            }
        }
       else if (peek == 'Z')
        RETURN_IF_ERROR (demangle_local_name (dm));
       else if (peek == 'I')
        {
-         if (*template_p)
-           return STATUS_INTERNAL_ERROR;
-         /* The template name is a substitution candidate.  */
-         RETURN_IF_ERROR (substitution_add (dm, start, 0, NOT_TEMPLATE_PARM));
          RETURN_IF_ERROR (demangle_template_args (dm));
-         *template_p = 1;
+
+         /* Now we want to indicate to the caller that we've
+            demangled template arguments, thus the prefix was a
+            <template-prefix>.  That's so that the caller knows to
+            demangle the function's return type, if this turns out to
+            be a function name.  But, if it's a member template
+            constructor or a templated conversion operator, report it
+            as untemplated.  Those never get encoded return types.  */
+         *encode_return_type = !suppress_return_type;
        }
       else if (peek == 'E')
        /* All done.  */
@@ -1168,38 +1278,56 @@ demangle_prefix (dm, template_p)
       else
        return "Unexpected character in <compound-name>.";
 
-      /* Add a new substitution for the prefix thus far.  */
-      RETURN_IF_ERROR (substitution_add (dm, start, *template_p, 
-                                        NOT_TEMPLATE_PARM));
+      if (peek != 'S'
+         && peek_char (dm) != 'E')
+       /* Add a new substitution for the prefix thus far.  */
+       RETURN_IF_ERROR (substitution_add (dm, start, *encode_return_type));
     }
 }
 
-/* Demangles and emits an <unqualified-name>.  If the
-   <unqualified-name> is a function and the first element in the
-   argument list should be taken to be its return type,
-   ENCODE_RETURN_TYPE is non-zero.
+/* Demangles and emits an <unqualified-name>.  If this
+   <unqualified-name> is for a special function type that should never
+   have its return type encoded (particularly, a constructor or
+   conversion operator), *SUPPRESS_RETURN_TYPE is set to 1; otherwise,
+   it is set to zero.
 
     <unqualified-name>  ::= <operator-name>
                        ::= <special-name>  
                        ::= <source-name>  */
 
 static status_t
-demangle_unqualified_name (dm)
+demangle_unqualified_name (dm, suppress_return_type)
      demangling_t dm;
+     int *suppress_return_type;
 {
   char peek = peek_char (dm);
 
   DEMANGLE_TRACE ("unqualified-name", dm);
 
+  /* By default, don't force suppression of the return type (though
+     non-template functions still don't get a return type encoded).  */ 
+  *suppress_return_type = 0;
+
   if (IS_DIGIT ((unsigned char) peek))
     RETURN_IF_ERROR (demangle_source_name (dm));
   else if (peek >= 'a' && peek <= 'z')
     {
       int num_args;
+
+      /* Conversion operators never have a return type encoded.  */
+      if (peek == 'c' && peek_char_next (dm) == 'v')
+       *suppress_return_type = 1;
+
       RETURN_IF_ERROR (demangle_operator_name (dm, 0, &num_args));
     }
   else if (peek == 'C' || peek == 'D')
-    RETURN_IF_ERROR (demangle_ctor_dtor_name (dm));
+    {
+      /* Constructors never have a return type encoded.  */
+      if (peek == 'C')
+       *suppress_return_type = 1;
+
+      RETURN_IF_ERROR (demangle_ctor_dtor_name (dm));
+    }
   else
     return "Unexpected character in <unqualified-name>.";
 
@@ -1229,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;
 }
@@ -1337,6 +1465,29 @@ demangle_identifier (dm, length, identifier)
        return STATUS_ALLOCATION_FAILED;
     }
 
+  /* GCC encodes anonymous namespaces using a `_GLOBAL_[_.$]N.'
+     followed by the source file name and some random characters.
+     Unless we're in strict mode, decipher these names appropriately.  */
+  if (!flag_strict)
+    {
+      char *name = dyn_string_buf (identifier);
+      int prefix_length = strlen (ANONYMOUS_NAMESPACE_PREFIX);
+
+      /* Compare the first, fixed part.  */
+      if (strncmp (name, ANONYMOUS_NAMESPACE_PREFIX, prefix_length) == 0)
+        {
+         name += prefix_length;
+         /* The next character might be a period, an underscore, or
+            dollar sign, depending on the target architecture's
+            assembler's capabilities.  After that comes an `N'.  */
+         if ((*name == '.' || *name == '_' || *name == '$')
+             && *(name + 1) == 'N')
+           /* This looks like the anonymous namespace identifier.
+              Replace it with something comprehensible.  */
+           dyn_string_copy_cstr (identifier, "(anonymous namespace)");
+       }
+    }
+
   return STATUS_OK;
 }
 
@@ -1395,7 +1546,7 @@ demangle_identifier (dm, length, identifier)
                   ::= qu        # ?
                   ::= sz        # sizeof 
                   ::= cv <type> # cast        
-                  ::= vx <source-name>  # vendor extended operator  */
+                 ::= v [0-9] <source-name>  # vendor extended operator  */
 
 static status_t
 demangle_operator_name (dm, short_name, num_args)
@@ -1406,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[] = 
@@ -1456,6 +1607,7 @@ demangle_operator_name (dm, short_name, num_args)
     { "pm", "->*"      , 2 },
     { "pp", "++"       , 1 },
     { "ps", "+"        , 1 },
+    { "pt", "->"       , 2 },
     { "qu", "?"        , 3 },
     { "rM", "%="       , 2 },
     { "rS", ">>="      , 2 },
@@ -1474,10 +1626,10 @@ demangle_operator_name (dm, short_name, num_args)
 
   DEMANGLE_TRACE ("operator-name", dm);
 
-  /* Is this a vendor extended operator?  */
-  if (c0 == 'v' && c1 == 'x')
+  /* 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;
@@ -1486,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;
@@ -1504,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;
@@ -1523,22 +1675,157 @@ demangle_operator_name (dm, short_name, num_args)
     }
 }
 
+/* Demangles and omits an <nv-offset>.
+
+    <nv-offset> ::= <offset number>   # non-virtual base override  */
+
+static status_t
+demangle_nv_offset (dm)
+     demangling_t dm;
+{
+  dyn_string_t number;
+  status_t status = STATUS_OK;
+
+  DEMANGLE_TRACE ("h-offset", dm);
+
+  /* Demangle the offset.  */
+  number = dyn_string_new (4);
+  if (number == NULL)
+    return STATUS_ALLOCATION_FAILED;
+  demangle_number_literally (dm, number, 10, 1);
+
+  /* Don't display the offset unless in verbose mode.  */
+  if (flag_verbose)
+    {
+      status = result_add (dm, " [nv:");
+      if (STATUS_NO_ERROR (status))
+       status = result_add_string (dm, number);
+      if (STATUS_NO_ERROR (status))
+       status = result_add_char (dm, ']');
+    }
+
+  /* Clean up.  */
+  dyn_string_delete (number);
+  RETURN_IF_ERROR (status);
+  return STATUS_OK;
+}
+
+/* Demangles and emits a <v-offset>. 
+
+    <v-offset>  ::= <offset number> _ <virtual offset number>
+                       # virtual base override, with vcall offset  */
+
+static status_t
+demangle_v_offset (dm)
+     demangling_t dm;
+{
+  dyn_string_t number;
+  status_t status = STATUS_OK;
+
+  DEMANGLE_TRACE ("v-offset", dm);
+
+  /* Demangle the offset.  */
+  number = dyn_string_new (4);
+  if (number == NULL)
+    return STATUS_ALLOCATION_FAILED;
+  demangle_number_literally (dm, number, 10, 1);
+
+  /* Don't display the offset unless in verbose mode.  */
+  if (flag_verbose)
+    {
+      status = result_add (dm, " [v:");
+      if (STATUS_NO_ERROR (status))
+       status = result_add_string (dm, number);
+      if (STATUS_NO_ERROR (status))
+       result_add_char (dm, ',');
+    }
+  dyn_string_delete (number);
+  RETURN_IF_ERROR (status);
+
+  /* Demangle the separator.  */
+  RETURN_IF_ERROR (demangle_char (dm, '_'));
+
+  /* Demangle the vcall offset.  */
+  number = dyn_string_new (4);
+  if (number == NULL)
+    return STATUS_ALLOCATION_FAILED;
+  demangle_number_literally (dm, number, 10, 1);
+
+  /* Don't display the vcall offset unless in verbose mode.  */
+  if (flag_verbose)
+    {
+      status = result_add_string (dm, number);
+      if (STATUS_NO_ERROR (status))
+       status = result_add_char (dm, ']');
+    }
+  dyn_string_delete (number);
+  RETURN_IF_ERROR (status);
+
+  return STATUS_OK;
+}
+
+/* Demangles and emits a <call-offset>.
+
+    <call-offset> ::= h <nv-offset> _
+                 ::= v <v-offset> _  */
+
+static status_t
+demangle_call_offset (dm)
+     demangling_t dm;
+{
+  DEMANGLE_TRACE ("call-offset", dm);
+
+  switch (peek_char (dm))
+    {
+    case 'h':
+      advance_char (dm);
+      /* Demangle the offset.  */
+      RETURN_IF_ERROR (demangle_nv_offset (dm));
+      /* Demangle the separator.  */
+      RETURN_IF_ERROR (demangle_char (dm, '_'));
+      break;
+
+    case 'v':
+      advance_char (dm);
+      /* Demangle the offset.  */
+      RETURN_IF_ERROR (demangle_v_offset (dm));
+      /* Demangle the separator.  */
+      RETURN_IF_ERROR (demangle_char (dm, '_'));
+      break;
+
+    default:
+      return "Unrecognized <call-offset>.";
+    }
+
+  return STATUS_OK;
+}
+
 /* Demangles and emits a <special-name>.  
 
     <special-name> ::= GV <object name>   # Guard variable
-                   ::= Th[n] <offset number> _ <base name> <base encoding>
-                                          # non-virtual base override thunk
-                   ::= Tv[n] <offset number> _ <vcall offset number> 
-                         _ <base encoding>
-                                          # virtual base override thunk
                    ::= TV <type>          # virtual table
                    ::= TT <type>          # VTT
                    ::= TI <type>          # typeinfo structure
                   ::= TS <type>          # typeinfo name  
 
+   Other relevant productions include thunks:
+
+    <special-name> ::= T <call-offset> <base encoding>
+                        # base is the nominal target function of thunk
+
+    <special-name> ::= Tc <call-offset> <call-offset> <base encoding>
+                        # base is the nominal target function of thunk
+                        # first call-offset is 'this' adjustment
+                        # second call-offset is result adjustment
+
+   where
+
+    <call-offset>  ::= h <nv-offset> _
+                  ::= v <v-offset> _
+
    Also demangles the special g++ manglings,
 
-    <special-name> ::= CT <type> <offset number> _ <base type>
+    <special-name> ::= TC <type> <offset number> _ <base type>
                                           # construction vtable
                   ::= TF <type>          # typeinfo function (old ABI only)
                   ::= TJ <type>          # java Class structure  */
@@ -1555,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')
     {
@@ -1573,108 +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"));
-         /* Demangle and emit the offset.  */
-         number = dyn_string_new (4);
-         if (number == NULL)
-           return STATUS_ALLOCATION_FAILED;
-         demangle_number_literally (dm, number, 10, 1);
-         /* Don't display the offset unless in verbose mode.  */
-         if (flag_verbose)
-           {
-             status = result_append_char (dm, ' ');
-             if (STATUS_NO_ERROR (status))
-               status = result_append_string (dm, number);
-           }
-         dyn_string_delete (number);
-         RETURN_IF_ERROR (status);
+         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 "));
-         /* Demangle and emit the offset.  */
-         number = dyn_string_new (4);
-         if (number == NULL)
-           return STATUS_ALLOCATION_FAILED;
-         demangle_number_literally (dm, number, 10, 1);
-         /* Don't display the offset unless in verbose mode.  */
-         if (flag_verbose)
-           {
-             status = result_append_string (dm, number);
-             if (STATUS_NO_ERROR (status))
-               result_append_char (dm, ' ');
-           }
-         dyn_string_delete (number);
-         RETURN_IF_ERROR (status);
-         /* Demangle the separator.  */
-         RETURN_IF_ERROR (demangle_char (dm, '_'));
-         /* Demangle and emit the vcall offset.  */
-         number = dyn_string_new (4);
-         if (number == NULL)
-           return STATUS_ALLOCATION_FAILED;
-         demangle_number_literally (dm, number, 10, 1);
-         /* Don't display the vcall offset unless in verbose mode.  */
-         if (flag_verbose)
-           {
-             status = result_append_string (dm, number);
-             if (STATUS_NO_ERROR (status))
-               status = result_append_char (dm, ' ');
-           }
-         dyn_string_delete (number);
-         RETURN_IF_ERROR (status);
+         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_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_add (dm, " to "));
          RETURN_IF_ERROR (demangle_encoding (dm));
          break;
 
@@ -1682,25 +1954,44 @@ demangle_special_name (dm)
          /* TC is a special g++ mangling for a construction vtable. */
          if (!flag_strict)
            {
+             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));
              RETURN_IF_ERROR (demangle_type (dm));
+             derived_type = (dyn_string_t) result_pop (dm);
+
              /* Demangle the offset.  */
              number = dyn_string_new (4);
              if (number == NULL)
-               return STATUS_ALLOCATION_FAILED;
+               {
+                 dyn_string_delete (derived_type);
+                 return STATUS_ALLOCATION_FAILED;
+               }
              demangle_number_literally (dm, number, 10, 1);
              /* Demangle the underscore separator.  */
-             RETURN_IF_ERROR (demangle_char (dm, '_'));
+             status = demangle_char (dm, '_');
+
              /* Demangle the base type.  */
-             RETURN_IF_ERROR (result_append (dm, "-in-"));
-             RETURN_IF_ERROR (demangle_type (dm));
+             if (STATUS_NO_ERROR (status))
+               status = demangle_type (dm);
+
+             /* Emit the derived type.  */
+             if (STATUS_NO_ERROR (status))
+               status = result_add (dm, "-in-");
+             if (STATUS_NO_ERROR (status))
+               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);
@@ -1724,7 +2015,6 @@ demangle_special_name (dm)
                    ::= C1  # complete object (in-charge) ctor
                    ::= C2  # base object (not-in-charge) ctor
                    ::= C3  # complete object (in-charge) allocating ctor
-                   ::= C4  # base object (not-in-charge) allocating ctor
                    ::= D0  # deleting (in-charge) dtor
                    ::= D1  # complete object (in-charge) dtor
                    ::= D2  # base object (not-in-charge) dtor  */
@@ -1737,8 +2027,7 @@ demangle_ctor_dtor_name (dm)
   {
     "in-charge",
     "not-in-charge",
-    "in-charge allocating",
-    "not-in-charge allocating"
+    "allocating"
   };
   static const char *const dtor_flavors[] = 
   {
@@ -1756,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) > '4')
+      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
@@ -1798,6 +2105,19 @@ demangle_ctor_dtor_name (dm)
    a pointer to data or pointer to function to construct the right
    output syntax.  C++'s pointer syntax is hairy.  
 
+   This function adds substitution candidates for every nested
+   pointer/reference type it processes, including the outermost, final
+   type, assuming the substitution starts at SUBSTITUTION_START in the
+   demangling result.  For example, if this function demangles
+   `PP3Foo', it will add a substitution for `Foo', `Foo*', and
+   `Foo**', in that order.
+
+   *INSERT_POS is a quantity used internally, when this function calls
+   itself recursively, to figure out where to insert pointer
+   punctuation on the way up.  On entry to this function, INSERT_POS
+   should point to a temporary value, but that value need not be
+   initialized.
+
      <type> ::= P <type>
             ::= R <type>
             ::= <pointer-to-member-type>
@@ -1805,105 +2125,154 @@ demangle_ctor_dtor_name (dm)
      <pointer-to-member-type> ::= M </class/ type> </member/ type>  */
 
 static status_t
-demangle_type_ptr (dm)
+demangle_type_ptr (dm, insert_pos, substitution_start)
      demangling_t dm;
+     int *insert_pos;
+     int substitution_start;
 {
-  char next;
   status_t status;
-
-  /* Collect pointer symbols into this string.  */
-  dyn_string_t symbols = dyn_string_new (10);
+  int is_substitution_candidate = 1;
 
   DEMANGLE_TRACE ("type*", dm);
 
-  if (symbols == NULL)
-    return STATUS_ALLOCATION_FAILED;
-
   /* Scan forward, collecting pointers and references into symbols,
      until we hit something else.  Then emit the type.  */
-  while (1)
+  switch (peek_char (dm))
     {
-      next = peek_char (dm);
-      if (next == 'P')
-       {
-         if (!dyn_string_append_char (symbols, '*'))
-           return STATUS_ALLOCATION_FAILED;
-         advance_char (dm);
-       }
-      else if (next == 'R')
-       {
-         if (!dyn_string_append_char (symbols, '&'))
-           return STATUS_ALLOCATION_FAILED;
-         advance_char (dm);
-       }
-      else if (next == 'M')
-       {
-         /* Pointer-to-member.  */
-         dyn_string_t class_type;
-
-         /* Eat the 'M'.  */
-         advance_char (dm);
-
-         /* Capture the type of which this is a pointer-to-member.  */
-         RETURN_IF_ERROR (result_push (dm));
-         RETURN_IF_ERROR (demangle_type (dm));
-         class_type = (dyn_string_t) result_pop (dm);
-
-         /* Build the pointer-to-member notation.  It comes before
-            other pointer and reference qualifiers -- */
-         if (!dyn_string_prepend_cstr (symbols, "::*"))
-           return STATUS_ALLOCATION_FAILED;
-         if (!dyn_string_prepend (symbols, class_type))
-           return STATUS_ALLOCATION_FAILED;
-         dyn_string_delete (class_type);
+    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.  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);
+      break;
 
-         if (peek_char (dm) == 'F')
-           continue;
+    case 'R':
+      /* A reference.  Snarf the `R'.  */
+      advance_char (dm);
+      /* Demangle the underlying type.  */
+      RETURN_IF_ERROR (demangle_type_ptr (dm, insert_pos, 
+                                         substitution_start));
+      /* Insert an ampersand where we're told to; it doesn't
+        necessarily go at the end.  */
+      RETURN_IF_ERROR (result_insert_char (dm, *insert_pos, '&'));
+      /* The next (outermost) pointer or reference character should go
+        after this one.  */
+      ++(*insert_pos);
+      break;
 
-         /* Demangle the type of the pointed-to member.  */
+    case 'M':
+    {
+      /* A pointer-to-member.  */
+      dyn_string_t class_type;
+      
+      /* Eat the 'M'.  */
+      advance_char (dm);
+      
+      /* Capture the type of which this is a pointer-to-member.  */
+      RETURN_IF_ERROR (result_push (dm));
+      RETURN_IF_ERROR (demangle_type (dm));
+      class_type = (dyn_string_t) result_pop (dm);
+      
+      if (peek_char (dm) == 'F')
+       /* A pointer-to-member function.  We want output along the
+          lines of `void (C::*) (int, int)'.  Demangle the function
+          type, which would in this case give `void () (int, int)'
+          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);
-         /* Add the pointer-to-member syntax, and other pointer and
-            reference symbols.  */
-         if (STATUS_NO_ERROR (status))
-           status = result_append_string (dm, symbols);
-         /* Clean up.  */
-         dyn_string_delete (symbols);
-
-         RETURN_IF_ERROR (status);
-         return STATUS_OK;
+         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_caret_pos (dm);
        }
-      else if (next == 'F')
-       {
-         /* Ooh, tricky, a pointer-to-function.  */
-         int position = result_length (dm);
-         status = result_append_char (dm, '(');
-         if (STATUS_NO_ERROR (status))
-           status = result_append_string (dm, symbols);
-         if (STATUS_NO_ERROR (status))
-           status = result_append_char (dm, ')');
-         dyn_string_delete (symbols);
-         RETURN_IF_ERROR (status);
 
-         RETURN_IF_ERROR (demangle_function_type (dm, position));
-         return STATUS_OK;
-       }
-      else
-       {
-         /* No more pointe or reference tokens.  Finish up.  */
-         status = demangle_type (dm);
+      /* Build the pointer-to-member notation.  */
+      if (STATUS_NO_ERROR (status))
+       status = result_insert (dm, *insert_pos, "::*");
+      if (STATUS_NO_ERROR (status))
+       status = result_insert_string (dm, *insert_pos, class_type);
+      /* There may be additional levels of (pointer or reference)
+        indirection in this type.  If so, the `*' and `&' should be
+        added after the pointer-to-member notation (e.g. `C::*&' for
+        a reference to a pointer-to-member of class C).  */
+      *insert_pos += dyn_string_length (class_type) + 3;
 
-         if (STATUS_NO_ERROR (status))
-           status = result_append_string (dm, symbols);
-         dyn_string_delete (symbols);
-         RETURN_IF_ERROR (status);
+      /* Clean up. */
+      dyn_string_delete (class_type);
 
-         RETURN_IF_ERROR (status);
-         return STATUS_OK;
-       }
+      RETURN_IF_ERROR (status);
+    }
+    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_caret_pos (dm);
+      /* The parentheses indicate this is a function pointer or
+        reference type.  */
+      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.  */
+      RETURN_IF_ERROR (demangle_function_type (dm, insert_pos));
+      /* We should now have something along the lines of 
+        `void () (int, int)'.  The pointer or reference characters
+        have to inside the first set of parentheses.  *insert_pos has
+        already been updated to point past the end of the return
+        type.  Move it one character over so it points inside the
+        `()'.  */
+      ++(*insert_pos);
+      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_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)
+    RETURN_IF_ERROR (substitution_add (dm, substitution_start, 0));
+  
+  return STATUS_OK;
 }
 
 /* Demangles and emits a <type>.  
@@ -1914,6 +2283,7 @@ demangle_type_ptr (dm)
           ::= <array-type>
           ::= <pointer-to-member-type>
           ::= <template-param>
+          ::= <template-template-param> <template-args>
            ::= <CV-qualifiers> <type>
           ::= P <type>   # pointer-to
           ::= R <type>   # reference-to
@@ -1928,22 +2298,29 @@ demangle_type (dm)
 {
   int start = substitution_start (dm);
   char peek = peek_char (dm);
-  int template_p = 0;
-  int special_std_substitution;
-  int is_builtin_type = 0;
+  char peek_next;
+  int encode_return_type = 0;
   template_arg_list_t old_arg_list = current_template_arg_list (dm);
-  int template_parm = NOT_TEMPLATE_PARM;
+  int insert_pos;
+
+  /* A <type> can be a <substitution>; therefore, this <type> is a
+     substitution candidate unless a special condition holds (see
+     below).  */
+  int is_substitution_candidate = 1;
 
   DEMANGLE_TRACE ("type", dm);
 
   /* A <class-enum-type> can start with a digit (a <source-name>), an
      N (a <nested-name>), or a Z (a <local-name>).  */
   if (IS_DIGIT ((unsigned char) peek) || peek == 'N' || peek == 'Z')
-    RETURN_IF_ERROR (demangle_class_enum_type (dm, &template_p));
-  else if (peek >= 'a' && peek <= 'z')
+    RETURN_IF_ERROR (demangle_class_enum_type (dm, &encode_return_type));
+  /* Lower-case letters begin <builtin-type>s, except for `r', which
+     denotes restrict.  */
+  else if (peek >= 'a' && peek <= 'z' && peek != 'r')
     {
       RETURN_IF_ERROR (demangle_builtin_type (dm));
-      is_builtin_type = 1;
+      /* Built-in types are not substitution candidates.  */
+      is_substitution_candidate = 0;
     }
   else
     switch (peek)
@@ -1951,37 +2328,36 @@ demangle_type (dm)
       case 'r':
       case 'V':
       case 'K':
+       /* CV-qualifiers (including restrict).  We have to demangle
+          them off to the side, since C++ syntax puts them in a funny
+          place for qualified pointer and reference types.  */
        {
          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;
 
@@ -1989,50 +2365,112 @@ 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':
-       RETURN_IF_ERROR (demangle_template_param (dm, &template_parm));
+       /* It's either a <template-param> or a
+          <template-template-param>.  In either case, demangle the
+          `T' token first.  */
+       RETURN_IF_ERROR (demangle_template_param (dm));
+
+       /* Check for a template argument list; if one is found, it's a
+            <template-template-param> ::= <template-param>
+                                       ::= <substitution>  */
+       if (peek_char (dm) == 'I')
+         {
+           /* Add a substitution candidate.  The template parameter
+              `T' token is a substitution candidate by itself,
+              without the template argument list.  */
+           RETURN_IF_ERROR (substitution_add (dm, start, encode_return_type));
+
+           /* Now demangle the template argument list.  */
+           RETURN_IF_ERROR (demangle_template_args (dm));
+           /* The entire type, including the template template
+              parameter and its argument list, will be added as a
+              substitution candidate below.  */
+         }
+
        break;
 
       case 'S':
-       RETURN_IF_ERROR (demangle_substitution (dm, &template_p,
-                                               &special_std_substitution));
-       if (special_std_substitution)
+       /* First check if this is a special substitution.  If it is,
+          this is a <class-enum-type>.  Special substitutions have a
+          letter following the `S'; other substitutions have a digit
+          or underscore.  */
+       peek_next = peek_char_next (dm);
+       if (IS_DIGIT (peek_next) || peek_next == '_')
+         {
+           RETURN_IF_ERROR (demangle_substitution (dm, &encode_return_type));
+           
+           /* The substituted name may have been a template name.
+              Check if template arguments follow, and if so, demangle
+              them.  */
+           if (peek_char (dm) == 'I')
+             RETURN_IF_ERROR (demangle_template_args (dm));
+           else
+             /* A substitution token is not itself a substitution
+                candidate.  (However, if the substituted template is
+                instantiated, the resulting type is.)  */
+             is_substitution_candidate = 0;
+         }
+       else
          {
-           /* This was the magic `std::' substitution.  What follows
-              must be a class name in that namespace.  */
-           RETURN_IF_ERROR (result_append (dm, "::"));
-           RETURN_IF_ERROR (demangle_class_enum_type (dm, &template_p));
+           /* 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;
 
       case 'P':
       case 'R':
       case 'M':
-       RETURN_IF_ERROR (demangle_type_ptr (dm));
+       RETURN_IF_ERROR (demangle_type_ptr (dm, &insert_pos, start));
+       /* demangle_type_ptr adds all applicable substitution
+          candidates.  */
+       is_substitution_candidate = 0;
        break;
 
       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;
 
       case 'U':
-       /* Vendor extended type qualifier.  */
+       /* 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;
 
@@ -2040,13 +2478,12 @@ demangle_type (dm)
        return "Unexpected character in <type>.";
       }
 
-  /* Unqualified builin types are not substitution candidates.  */
-  if (!is_builtin_type)
+  if (is_substitution_candidate)
     /* Add a new substitution for the type. If this type was a
        <template-param>, pass its index since from the point of
-       substitutions, a <template-param> token is a substitution
+       substitutions; a <template-param> token is a substitution
        candidate distinct from the type that is substituted for it.  */
-    RETURN_IF_ERROR (substitution_add (dm, start, template_p, template_parm));
+    RETURN_IF_ERROR (substitution_add (dm, start, encode_return_type));
 
   /* Pop off template argument lists added during mangling of this
      type.  */
@@ -2087,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
@@ -2129,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;
     }
@@ -2185,16 +2660,18 @@ demangle_CV_qualifiers (dm, qualifiers)
     }
 }
 
-/* Demangles and emits a <function-type> FUNCTION_NAME_POS is the
+/* Demangles and emits a <function-type>.  *FUNCTION_NAME_POS is the
    position in the result string of the start of the function
-   identifier, at which the function's return type will be inserted.  
+   identifier, at which the function's return type will be inserted;
+   *FUNCTION_NAME_POS is updated to position past the end of the
+   function's return type.
 
     <function-type> ::= F [Y] <bare-function-type> E  */
 
 static status_t
 demangle_function_type (dm, function_name_pos)
      demangling_t dm;
-     int function_name_pos;
+     int *function_name_pos;
 {
   DEMANGLE_TRACE ("function-type", dm);
   RETURN_IF_ERROR (demangle_char (dm, 'F'));  
@@ -2202,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));
@@ -2220,7 +2697,7 @@ demangle_function_type (dm, function_name_pos)
 static status_t
 demangle_bare_function_type (dm, return_type_pos)
      demangling_t dm;
-     int return_type_pos;
+     int *return_type_pos;
 {
   /* Sequence is the index of the current function parameter, counting
      from zero.  The value -1 denotes the return type.  */
@@ -2229,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)
@@ -2245,10 +2722,16 @@ demangle_bare_function_type (dm, return_type_pos)
 
          /* Add a space to the end of the type.  Insert the return
              type where we've been asked to. */
-         if (!dyn_string_append_space (return_type) 
-             || !dyn_string_insert (result_string (dm), return_type_pos, 
-                                    return_type))
+         if (!dyn_string_append_space (return_type))
            status = STATUS_ALLOCATION_FAILED;
+         if (STATUS_NO_ERROR (status))
+           {
+             if (!dyn_string_insert (result_string (dm), *return_type_pos, 
+                                     return_type))
+               status = STATUS_ALLOCATION_FAILED;
+             else
+               *return_type_pos += dyn_string_length (return_type);
+           }
 
          dyn_string_delete (return_type);
          RETURN_IF_ERROR (status);
@@ -2259,60 +2742,103 @@ demangle_bare_function_type (dm, return_type_pos)
             the only type in a parameter list; in that case, we want
             to print `foo ()' instead of `foo (void)'.  */
          if (peek_char (dm) == 'v')
+           /* Consume the v.  */
+           advance_char (dm);
+         else
            {
-             /* Consume the v.  */
-             advance_char (dm);
-             continue;
+             /* Separate parameter types by commas.  */
+             if (sequence > 0)
+               RETURN_IF_ERROR (result_add (dm, ", "));
+             /* Demangle the type.  */
+             RETURN_IF_ERROR (demangle_type (dm));
            }
-         /* Separate parameter types by commas.  */
-         if (sequence > 0)
-           RETURN_IF_ERROR (result_append (dm, ", "));
-         /* Demangle the type.  */
-         RETURN_IF_ERROR (demangle_type (dm));
        }
 
       ++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
+     return type, if we were supposed to demangle that.  */
+  if (sequence == -1)
+    return "Missing function return type.";
+  else if (sequence == 0)
+    return "Missing function parameter.";
 
   return STATUS_OK;
 }
 
-/* Demangles and emits a <class-enum-type>.  *TEMPLATE_P is set to
+/* Demangles and emits a <class-enum-type>.  *ENCODE_RETURN_TYPE is set to
    non-zero if the type is a template-id, zero otherwise.  
 
     <class-enum-type> ::= <name>  */
 
 static status_t
-demangle_class_enum_type (dm, template_p)
+demangle_class_enum_type (dm, encode_return_type)
      demangling_t dm;
-     int *template_p;
+     int *encode_return_type;
 {
   DEMANGLE_TRACE ("class-enum-type", dm);
 
-  RETURN_IF_ERROR (demangle_name (dm, template_p));
+  RETURN_IF_ERROR (demangle_name (dm, encode_return_type));
   return STATUS_OK;
 }
 
 /* Demangles and emits an <array-type>.  
 
-    <array-type> ::= A [<dimension number>] _ <element 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;
-  dyn_string_t array_size = dyn_string_new (10);
+  status_t status = STATUS_OK;
+  dyn_string_t array_size = NULL;
+  char peek;
 
-  if (array_size == NULL)
-    return STATUS_ALLOCATION_FAILED;
+  DEMANGLE_TRACE ("array-type", dm);
 
-  status = demangle_char (dm, 'A');
+  RETURN_IF_ERROR (demangle_char (dm, 'A'));
 
   /* Demangle the array size into array_size.  */
-  if (STATUS_NO_ERROR (status))
-    status = demangle_number_literally (dm, array_size, 10, 0);
+  peek = peek_char (dm);
+  if (peek == '_')
+    /* Array bound is omitted.  This is a C99-style VLA.  */
+    ;
+  else if (IS_DIGIT (peek_char (dm))) 
+    {
+      /* It looks like a constant array bound.  */
+      array_size = dyn_string_new (10);
+      if (array_size == NULL)
+       return STATUS_ALLOCATION_FAILED;
+      status = demangle_number_literally (dm, array_size, 10, 0);
+    }
+  else
+    {
+      /* Anything is must be an expression for a nont-constant array
+        bound.  This happens if the array type occurs in a template
+        and the array bound references a template parameter.  */
+      RETURN_IF_ERROR (result_push (dm));
+      RETURN_IF_ERROR (demangle_expression (dm));
+      array_size = (dyn_string_t) result_pop (dm);
+    }
+  /* array_size may have been allocated by now, so we can't use
+     RETURN_IF_ERROR until it's been deallocated.  */
 
   /* Demangle the base type of the array.  */
   if (STATUS_NO_ERROR (status))
@@ -2320,30 +2846,40 @@ 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_add_string (dm, array_size);
   if (STATUS_NO_ERROR (status))
-    status = result_append_string (dm, array_size);
-  if (STATUS_NO_ERROR (status))
-    status = result_append_char (dm, ']');
-  dyn_string_delete (array_size);
+    status = result_add_char (dm, ']');
+  if (array_size != NULL)
+    dyn_string_delete (array_size);
   
   RETURN_IF_ERROR (status);
 
   return STATUS_OK;
 }
 
-/* Demangles and emits a <template-param>.  The zero-indexed position
-   in the parameter list is placed in *TEMPLATE_PARM_NUMBER.  
+/* Demangles and emits a <template-param>.  
 
     <template-param> ::= T_       # first template parameter
                      ::= T <parameter-2 number> _  */
 
 static status_t
-demangle_template_param (dm, template_parm_number)
+demangle_template_param (dm)
      demangling_t dm;
-     int *template_parm_number;
 {
   int parm_number;
   template_arg_list_t current_arg_list = current_template_arg_list (dm);
@@ -2371,12 +2907,8 @@ demangle_template_param (dm, template_parm_number)
     /* 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));
-
-  if (peek_char (dm) == 'I')
-    RETURN_IF_ERROR (demangle_template_args (dm));
+  RETURN_IF_ERROR (result_add_string (dm, (dyn_string_t) arg));
 
-  *template_parm_number = parm_number;
   return STATUS_OK;
 }
 
@@ -2405,7 +2937,7 @@ demangle_template_args (dm)
     return STATUS_ALLOCATION_FAILED;
 
   RETURN_IF_ERROR (demangle_char (dm, 'I'));
-  RETURN_IF_ERROR (result_append_char (dm, '<'));
+  RETURN_IF_ERROR (result_open_template_list (dm));
   do
     {
       string_list_t arg;
@@ -2413,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));
@@ -2421,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);
@@ -2497,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.  */
@@ -2517,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);
@@ -2530,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)
@@ -2540,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);
 
@@ -2581,6 +3113,7 @@ demangle_template_arg (dm)
       /* Expression.  */
       advance_char (dm);
       RETURN_IF_ERROR (demangle_expression (dm));
+      RETURN_IF_ERROR (demangle_char (dm, 'E'));
       break;
 
     default:
@@ -2627,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, ')'));
        }
     }
 
@@ -2669,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;
 }
@@ -2685,12 +3218,11 @@ demangle_expr_primary (dm)
      demangling_t dm;
 {
   char peek = peek_char (dm);
-  int unused;
 
   DEMANGLE_TRACE ("expr-primary", dm);
 
   if (peek == 'T')
-    RETURN_IF_ERROR (demangle_template_param (dm, &unused));
+    RETURN_IF_ERROR (demangle_template_param (dm));
   else if (peek == 'L')
     {
       /* Consume the `L'.  */
@@ -2711,10 +3243,7 @@ demangle_expr_primary (dm)
 }
 
 /* Demangles and emits a <substitution>.  Sets *TEMPLATE_P to non-zero
-   if the substitution is the name of a template, zero otherwise.  If
-   the substitution token is St, which corresponds to the `::std::'
-   namespace and can appear in a non-nested name, sets
-   *SPECIAL_STD_SUBSTITUTION to non-zero; zero otherwise.  
+   if the substitution is the name of a template, zero otherwise. 
 
      <substitution> ::= S <seq-id> _
                     ::= S_
@@ -2730,14 +3259,13 @@ demangle_expr_primary (dm)
                     ::= So   # ::std::basic_ostream<char,  
                                                     std::char_traits<char> >
                     ::= Sd   # ::std::basic_iostream<char, 
-                                                     std::char_traits<char> >
+                                                    std::char_traits<char> >
 */
 
 static status_t
-demangle_substitution (dm, template_p, special_std_substitution)
+demangle_substitution (dm, template_p)
      demangling_t dm;
      int *template_p;
-     int *special_std_substitution;
 {
   int seq_id;
   int peek;
@@ -2746,7 +3274,6 @@ demangle_substitution (dm, template_p, special_std_substitution)
   DEMANGLE_TRACE ("substitution", dm);
 
   RETURN_IF_ERROR (demangle_char (dm, 'S'));
-  *special_std_substitution = 0;
 
   /* Scan the substitution sequence index.  A missing number denotes
      the first index.  */
@@ -2766,70 +3293,75 @@ demangle_substitution (dm, template_p, special_std_substitution)
       switch (peek)
        {
        case 't':
-         RETURN_IF_ERROR (result_append (dm, "std"));
-         *special_std_substitution = 1;
+         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;
          
        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;
          break;
 
        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;
          break;
 
        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;
          break;
 
        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;
          break;
 
        default:
@@ -2853,11 +3385,11 @@ demangle_substitution (dm, template_p, special_std_substitution)
      substitution, `S0_' is the second-most-recent, etc., shift the
      numbering by one.  */
   text = substitution_get (dm, seq_id + 1, template_p);
-  if (text == NULL)
+  if (text == NULL) 
     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;
@@ -2877,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, "'s "));
+  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));
@@ -2890,7 +3422,6 @@ demangle_local_name (dm)
   else
     {
       int unused;
-      RETURN_IF_ERROR (result_append (dm, "local "));
       /* Local name for some other entity.  Demangle its name.  */
       RETURN_IF_ERROR (demangle_name (dm, &unused));
       RETURN_IF_ERROR (demangle_discriminator (dm, 1));
@@ -2913,14 +3444,14 @@ demangle_discriminator (dm, suppress_first)
      int suppress_first;
 {
   /* Output for <discriminator>s to the demangled name is completely
-     supressed if not in verbose mode.  */
+     suppressed if not in verbose mode.  */
 
   if (peek_char (dm) == '_')
     {
       /* 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)))
        {
@@ -2931,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;
@@ -2957,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;
 
@@ -3005,13 +3533,14 @@ cp_demangle (name, result)
    dyn_string_t.  On success, returns STATUS_OK.  On failiure, returns
    an error message, and the contents of RESULT are unchanged.  */
 
+#ifdef IN_LIBGCC2
 static status_t
 cp_demangle_type (type_name, result)
      const char* type_name;
      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;
@@ -3042,12 +3571,9 @@ cp_demangle_type (type_name, result)
   return status;
 }
 
-
-#ifdef IN_LIBGCC2
-
 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.  
 
@@ -3115,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);
@@ -3161,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.  */
     {
@@ -3190,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"
@@ -3201,7 +3921,8 @@ static void print_usage
 
 /* Non-zero if CHAR is a character than can occur in a mangled name.  */
 #define is_mangled_char(CHAR)                                           \
-  (IS_ALPHA (CHAR) || IS_DIGIT (CHAR) || (CHAR) == '_')
+  (IS_ALPHA (CHAR) || IS_DIGIT (CHAR)                                   \
+   || (CHAR) == '_' || (CHAR) == '.' || (CHAR) == '$')
 
 /* The name of this program, as invoked.  */
 const char* program_name;
@@ -3214,7 +3935,7 @@ print_usage (fp, exit_value)
      int exit_value;
 {
   fprintf (fp, "Usage: %s [options] [names ...]\n", program_name);
-  fprintf (fp, "Options:\n", program_name);
+  fprintf (fp, "Options:\n");
   fprintf (fp, "  -h,--help       Display this message.\n");
   fprintf (fp, "  -s,--strict     Demangle standard names only.\n");
   fprintf (fp, "  -v,--verbose    Produce verbose demanglings.\n");
@@ -3224,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' },
@@ -3326,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.  */
@@ -3365,16 +4086,16 @@ 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))
            printf ("%s\n", dyn_string_buf (result));
          /* Abort on allocaiton failures.  */
-         if (status == STATUS_ALLOCATION_FAILED)
+         else if (status == STATUS_ALLOCATION_FAILED)
            {
-             fprintf (stderr, "Memory allocaiton failed.\n");
-             abort ():
+             fprintf (stderr, "Memory allocation failed.\n");
+             abort ();
            }
          /* If not, print the error message to stderr instead.  */
          else