[multiple changes]
authorBenjamin Kosnik <bkoz@gcc.gnu.org>
Fri, 27 Feb 2004 03:21:16 +0000 (03:21 +0000)
committerBenjamin Kosnik <bkoz@gcc.gnu.org>
Fri, 27 Feb 2004 03:21:16 +0000 (03:21 +0000)
2004-02-26  Ian Lance Taylor  <ian@wasabisystems.com>

* testsuite/demangle/abi_examples/01.cc: Expect error -2.
* testsuite/demangle/abi_examples/02.cc: Likewise.
* testsuite/demangle/regression/cw-11.cc: Likewise.
* testsuite/demangle/regression/cw-16.cc: Change two expected
results to match libiberty demangler output.

2004-02-26  Benjamin Kosnik  <bkoz@redhat.com>

PR libstdc++/10246
        * libsupc++/Makefile.am: Use libiberty demangler.
        (c_sources): Add cp-demangle.c.
        * libsupc++/Makefile.in: Regenerate.
        * src/Makefile.am (sources): Remove demangle.cc.
        * src/Makefile.in: Regenerate.
        * include/Makefile.am (bits_headers): Move demangle.h.
(ext_headers): ...here.
        * include/Makefile.in: Regenerate.
        * include/bits/demangle.h: Move...
* include/ext/demangle.h: ...here.
        * src/demangle.cc: Remove.

2004-02-26  Benjamin Kosnik  <bkoz@redhat.com>

        * include/bits/demangle.h: Add type template parameter to all
        templates with just an Allocator template parameter.

From-SVN: r78553

14 files changed:
libstdc++-v3/ChangeLog
libstdc++-v3/include/Makefile.am
libstdc++-v3/include/Makefile.in
libstdc++-v3/include/bits/demangle.h [deleted file]
libstdc++-v3/include/ext/demangle.h [new file with mode: 0644]
libstdc++-v3/libsupc++/Makefile.am
libstdc++-v3/libsupc++/Makefile.in
libstdc++-v3/src/Makefile.am
libstdc++-v3/src/Makefile.in
libstdc++-v3/src/demangle.cc [deleted file]
libstdc++-v3/testsuite/demangle/abi_examples/01.cc
libstdc++-v3/testsuite/demangle/abi_examples/02.cc
libstdc++-v3/testsuite/demangle/regression/cw-11.cc
libstdc++-v3/testsuite/demangle/regression/cw-16.cc

index 39782cd3d796bf035b181eef07e24f92fd56e6f4..00f732891108bab8ad2a6490ded0d9038f4e3dd7 100644 (file)
@@ -1,3 +1,31 @@
+2004-02-26  Ian Lance Taylor  <ian@wasabisystems.com>
+
+       * testsuite/demangle/abi_examples/01.cc: Expect error -2.
+       * testsuite/demangle/abi_examples/02.cc: Likewise.
+       * testsuite/demangle/regression/cw-11.cc: Likewise.
+       * testsuite/demangle/regression/cw-16.cc: Change two expected
+       results to match libiberty demangler output.
+
+2004-02-26  Benjamin Kosnik  <bkoz@redhat.com>
+
+       PR libstdc++/10246
+        * libsupc++/Makefile.am: Use libiberty demangler.
+        (c_sources): Add cp-demangle.c.
+        * libsupc++/Makefile.in: Regenerate.
+        * src/Makefile.am (sources): Remove demangle.cc.
+        * src/Makefile.in: Regenerate.
+        * include/Makefile.am (bits_headers): Move demangle.h.
+       (ext_headers): ...here.
+        * include/Makefile.in: Regenerate.
+        * include/bits/demangle.h: Move...
+       * include/ext/demangle.h: ...here.
+        * src/demangle.cc: Remove.
+
+2004-02-26  Benjamin Kosnik  <bkoz@redhat.com>
+        * include/bits/demangle.h: Add type template parameter to all
+        templates with just an Allocator template parameter.
 2004-02-25  Benjamin Kosnik  <bkoz@redhat.com>
 
        * include/bits/atomicity.h: New, forward declarations for __atomic_add
index b6b05ff30d8f22348abcc9b1327c8e8e8c64259c..63867322f6a791bf0fc2234d9ff27e946c99ca51 100644 (file)
@@ -103,7 +103,6 @@ bits_headers = \
        ${bits_srcdir}/concept_check.h \
        ${bits_srcdir}/concurrence.h \
        ${bits_srcdir}/cpp_type_traits.h \
-       ${bits_srcdir}/demangle.h \
        ${bits_srcdir}/deque.tcc \
        ${bits_srcdir}/fstream.tcc \
        ${bits_srcdir}/functexcept.h \
@@ -204,6 +203,7 @@ ext_builddir = ./ext
 ext_headers = \
        ${ext_srcdir}/algorithm \
        ${ext_srcdir}/debug_allocator.h \
+       ${ext_srcdir}/demangle.h \
        ${ext_srcdir}/enc_filebuf.h \
        ${ext_srcdir}/stdio_filebuf.h \
        ${ext_srcdir}/stdio_sync_filebuf.h \
index e0fa5c06c7b75293f4ad0de26b7a30fa9d06e59c..d79cded347fd2ef353c36f7892a923fa09836c1f 100644 (file)
@@ -299,7 +299,6 @@ bits_headers = \
        ${bits_srcdir}/concept_check.h \
        ${bits_srcdir}/concurrence.h \
        ${bits_srcdir}/cpp_type_traits.h \
-       ${bits_srcdir}/demangle.h \
        ${bits_srcdir}/deque.tcc \
        ${bits_srcdir}/fstream.tcc \
        ${bits_srcdir}/functexcept.h \
@@ -402,6 +401,7 @@ ext_builddir = ./ext
 ext_headers = \
        ${ext_srcdir}/algorithm \
        ${ext_srcdir}/debug_allocator.h \
+       ${ext_srcdir}/demangle.h \
        ${ext_srcdir}/enc_filebuf.h \
        ${ext_srcdir}/stdio_filebuf.h \
        ${ext_srcdir}/stdio_sync_filebuf.h \
@@ -526,9 +526,9 @@ debug_headers = \
 # For --enable-cheaders=c_std
 @GLIBCXX_C_HEADERS_C_STD_TRUE@c_base_headers_extra = ${c_base_srcdir}/cmath.tcc
 @GLIBCXX_C_HEADERS_C_STD_FALSE@c_base_headers_extra = 
-@GLIBCXX_C_HEADERS_COMPATIBILITY_FALSE@c_compatibility_headers_extra = 
 
 @GLIBCXX_C_HEADERS_COMPATIBILITY_TRUE@c_compatibility_headers_extra = ${c_compatibility_headers}
+@GLIBCXX_C_HEADERS_COMPATIBILITY_FALSE@c_compatibility_headers_extra = 
 
 host_srcdir = ${glibcxx_srcdir}/$(OS_INC_SRCDIR)
 host_builddir = ./${host_alias}/bits
diff --git a/libstdc++-v3/include/bits/demangle.h b/libstdc++-v3/include/bits/demangle.h
deleted file mode 100644 (file)
index 68cdabe..0000000
+++ /dev/null
@@ -1,2789 +0,0 @@
-// C++ IA64 / g++ v3 demangler  -*- C++ -*-
-
-// Copyright (C) 2003 Free Software Foundation, Inc.
-// Written by Carlo Wood <carlo@alinoe.com>
-//
-// This file is part of the GNU ISO C++ Library.  This library is free
-// software; you can redistribute it and/or modify it under the
-// terms of the GNU General Public License as published by the
-// Free Software Foundation; either version 2, or (at your option)
-// any later version.
-
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License along
-// with this library; see the file COPYING.  If not, write to the Free
-// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-
-// As a special exception, you may use this file as part of a free software
-// library without restriction.  Specifically, if other files instantiate
-// templates or use macros or inline functions from this file, or you compile
-// this file and link it with other files to produce an executable, this
-// file does not by itself cause the resulting executable to be covered by
-// the GNU General Public License.  This exception does not however
-// invalidate any other reasons why the executable file might be covered by
-// the GNU General Public License.
-
-// This file implements demangling of "C++ ABI for Itanium"-mangled symbol
-// and type names as described in Revision 1.73 of the C++ ABI as can be found
-// at http://www.codesourcery.com/cxx-abi/abi.html#mangling
-
-#ifndef _DEMANGLER_H
-#define _DEMANGLER_H 1
-
-#include <vector>
-#include <string>
-
-#ifndef _GLIBCXX_DEMANGLER_DEBUG
-#define _GLIBCXX_DEMANGLER_CWDEBUG 0
-#define _GLIBCXX_DEMANGLER_DEBUG(x)
-#define _GLIBCXX_DEMANGLER_DOUT(cntrl, data)
-#define _GLIBCXX_DEMANGLER_DOUT_ENTERING(x)
-#define _GLIBCXX_DEMANGLER_DOUT_ENTERING2(x)
-#define _GLIBCXX_DEMANGLER_DOUT_ENTERING3(x)
-#define _GLIBCXX_DEMANGLER_RETURN return M_result
-#define _GLIBCXX_DEMANGLER_RETURN2 return M_result
-#define _GLIBCXX_DEMANGLER_RETURN3
-#define _GLIBCXX_DEMANGLER_FAILURE \
-    do { M_result = false; return false; } while(0)
-#else
-#define _GLIBCXX_DEMANGLER_CWDEBUG 1
-#endif
-
-namespace __gnu_cxx
-{
-  namespace demangler
-  {
-
-    enum substitution_nt
-    {
-      type,
-      template_template_param,
-      nested_name_prefix,
-      nested_name_template_prefix,
-      unscoped_template_name
-    };
-
-    struct substitution_st
-    {
-      int M_start_pos;
-      substitution_nt M_type;
-      int M_number_of_prefixes;
-
-      substitution_st(int start_pos,
-                     substitution_nt type,
-                     int number_of_prefixes)
-      : M_start_pos(start_pos), M_type(type),
-       M_number_of_prefixes(number_of_prefixes)
-      { }
-    };
-
-    enum simple_qualifier_nt
-    {
-      complex_or_imaginary = 'G',
-      pointer = 'P',
-      reference = 'R'
-    };
-
-    enum cv_qualifier_nt
-    {
-      cv_qualifier = 'K'
-    };
-
-    enum param_qualifier_nt
-    {
-      vendor_extension = 'U',
-      array = 'A',
-      pointer_to_member = 'M'
-    };
-
-    template<typename Allocator>
-      class qualifier;
-
-    template<typename Allocator>
-      class qualifier_list;
-
-    template<typename Allocator>
-      class session;
-
-    template<typename Allocator>
-      class qualifier
-      {
-       typedef typename Allocator::template rebind<char>::other
-               char_Allocator;
-       typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
-           string_type;
-
-      private:
-       char M_qualifier1;
-       char M_qualifier2;
-       char M_qualifier3;
-       mutable unsigned char M_cnt;
-       string_type M_optional_type;
-       int M_start_pos;
-       bool M_part_of_substitution;
-
-      public:
-       qualifier(int start_pos,
-                 simple_qualifier_nt simple_qualifier,
-                 int inside_substitution)
-       : M_qualifier1(simple_qualifier),
-         M_start_pos(start_pos),
-         M_part_of_substitution(inside_substitution)
-       { }
-
-       qualifier(int start_pos,
-                 cv_qualifier_nt,
-                 char const* start,
-                 int count,
-                 int inside_substitution)
-       : M_qualifier1(start[0]),
-         M_qualifier2((count > 1) ? start[1] : '\0'),
-         M_qualifier3((count > 2) ? start[2] : '\0'),
-         M_start_pos(start_pos),
-         M_part_of_substitution(inside_substitution)
-       { }
-
-       qualifier(int start_pos,
-                 param_qualifier_nt param_qualifier,
-                 string_type optional_type,
-                 int inside_substitution)
-       : M_qualifier1(param_qualifier),
-         M_optional_type(optional_type),
-         M_start_pos(start_pos),
-         M_part_of_substitution(inside_substitution)
-       { }
-
-       int
-       get_start_pos(void) const
-       { return M_start_pos; }
-
-       char
-       first_qualifier(void) const
-       { M_cnt = 1; return M_qualifier1; }
-
-       char
-       next_qualifier(void) const
-       {
-         return (++M_cnt == 2) ? M_qualifier2
-                               : ((M_cnt == 3) ? M_qualifier3 : 0);
-       }
-
-       string_type const&
-       get_optional_type(void) const
-       { return M_optional_type; }
-
-       bool
-       part_of_substitution(void) const
-       { return M_part_of_substitution; }
-
-#if _GLIBCXX_DEMANGLER_CWDEBUG
-       friend std::ostream& operator<<(std::ostream& os, qualifier const& qual)
-       {
-         os << (char)qual.M_qualifier1;
-         if (qual.M_qualifier1 == vendor_extension ||
-             qual.M_qualifier1 == array ||
-             qual.M_qualifier1 == pointer_to_member)
-           os << " [" << qual.M_optional_type << ']';
-         else if (qual.M_qualifier1 == 'K' ||
-                  qual.M_qualifier1 == 'V' ||
-                  qual.M_qualifier1 == 'r')
-         {
-           if (qual.M_qualifier2)
-           {
-             os << (char)qual.M_qualifier2;
-             if (qual.M_qualifier3)
-               os << (char)qual.M_qualifier3;
-           }
-         }
-         return os;
-       }
-#endif
-      };
-
-    template<typename Allocator>
-      class qualifier_list
-      {
-       typedef typename Allocator::template rebind<char>::other
-         char_Allocator;
-       typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
-         string_type;
-
-      private:
-       mutable bool M_printing_suppressed;
-       typedef qualifier<Allocator> qual;
-        typedef typename Allocator::template rebind<qual>::other qual_Allocator;
-       typedef std::vector<qual, qual_Allocator> qual_vector;
-       qual_vector M_qualifier_starts;
-       session<Allocator>& M_demangler;
-
-       void decode_KVrA(string_type& prefix, string_type& postfix, int cvq,
-                        typename qual_vector::
-                          const_reverse_iterator const& iter_array) const;
-
-      public:
-       qualifier_list(session<Allocator>& demangler_obj)
-       : M_printing_suppressed(false), M_demangler(demangler_obj)
-       { }
-
-       void
-       add_qualifier_start(simple_qualifier_nt simple_qualifier,
-                           int start_pos,
-                           int inside_substitution)
-       { M_qualifier_starts.
-             push_back(qualifier<Allocator>(start_pos,
-                 simple_qualifier, inside_substitution)); }
-
-       void
-       add_qualifier_start(cv_qualifier_nt cv_qualifier,
-                           int start_pos,
-                           int count,
-                           int inside_substitution)
-       { M_qualifier_starts.
-             push_back(qualifier<Allocator>(start_pos,
-                   cv_qualifier, &M_demangler.M_str[start_pos],
-                   count, inside_substitution)); }
-
-       void
-       add_qualifier_start(param_qualifier_nt param_qualifier,
-                           int start_pos,
-                           string_type optional_type,
-                           int inside_substitution)
-       { M_qualifier_starts.
-             push_back(qualifier<Allocator>(start_pos,
-                   param_qualifier, optional_type, inside_substitution)); }
-
-       void
-       decode_qualifiers(string_type& prefix,
-                         string_type& postfix,
-                         bool member_function_pointer_qualifiers) const;
-
-       bool
-       suppressed(void) const
-       { return M_printing_suppressed; }
-
-       void
-       printing_suppressed(void)
-       { M_printing_suppressed = true; }
-
-       size_t
-       size(void) const
-       { return M_qualifier_starts.size(); }
-
-#if _GLIBCXX_DEMANGLER_CWDEBUG
-       friend std::ostream& operator<<(std::ostream& os, qualifier_list const& list)
-       {
-         typename qual_vector::const_iterator
-             iter = list.M_qualifier_starts.begin();
-         if (iter != list.M_qualifier_starts.end())
-         {
-           os << "{ " << *iter;
-           while (++iter != list.M_qualifier_starts.end())
-             os << ", " << *iter;
-           os << " }";
-         }
-         else
-           os << "{ }";
-         return os;
-       }
-#endif
-      };
-
-    struct implementation_details
-    {
-      private:
-        unsigned int M_style;
-
-      public:
-       // The following flags change the behaviour of the demangler.  The
-       // default behaviour is that none of these flags is set.
-
-        static unsigned int const style_void = 1;
-       // Default behaviour:                           int f()
-       // Use (void) instead of ():                    int f(void)
-
-        static unsigned int const style_literal = 2;
-       // Default behaviour:                           (long)13,
-       //                                              (unsigned long long)19
-       // Use extensions 'u', 'l' and 'll' for integral
-       // literals (as in template arguments):         13l, 19ull
-
-        static unsigned int const style_literal_int = 4;
-       // Default behaviour:                           4
-       // Use also an explicit
-       //   cast for int in literals:                  (int)4
-
-        static unsigned int const style_compact_expr_ops = 8;
-       // Default behaviour:                           (i) < (3), sizeof (int)
-       // Don't output spaces around
-       //   operators in expressions:                  (i)<(3), sizeof(int)
-
-        static unsigned int const style_sizeof_typename = 16;
-       // Default behaviour:                           sizeof (X::t)
-       // Put 'typename' infront of <nested-name>
-       // types inside a 'sizeof':                     sizeof (typename X::t)
-
-      public:
-       implementation_details(unsigned int style_flags = 0) :
-           M_style(style_flags) { }
-       virtual ~implementation_details() { }
-       bool get_style_void(void) const
-           { return (M_style & style_void); }
-       bool get_style_literal(void) const
-           { return (M_style & style_literal); }
-       bool get_style_literal_int(void) const
-           { return (M_style & style_literal_int); }
-       bool get_style_compact_expr_ops(void) const
-           { return (M_style & style_compact_expr_ops); }
-       bool get_style_sizeof_typename(void) const
-           { return (M_style & style_sizeof_typename); }
-        // This can be overridden by user implementations.
-        virtual bool decode_real(char* /* output */, unsigned long* /* input */,
-                                size_t /* size_of_real */) const
-            { return false; }
-    };
-
-    template<typename Allocator>
-      class session
-      {
-       friend class qualifier_list<Allocator>;
-       typedef typename Allocator::template rebind<char>::other
-           char_Allocator;
-       typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
-           string_type;
-
-      private:
-       char const* M_str;
-       int M_pos;
-       int M_maxpos;
-       bool M_result;
-       int M_inside_template_args;
-       int M_inside_type;
-       int M_inside_substitution;
-       bool M_saw_destructor;
-       bool M_name_is_cdtor;
-       bool M_name_is_template;
-       bool M_name_is_conversion_operator;
-       bool M_template_args_need_space;
-       string_type M_function_name;
-        typedef typename Allocator::template rebind<int>::other
-                int_Allocator;
-        typedef typename Allocator::template rebind<substitution_st>::other
-                subst_Allocator;
-       std::vector<int, int_Allocator> M_template_arg_pos;
-       int M_template_arg_pos_offset;
-       std::vector<substitution_st, subst_Allocator> M_substitutions_pos;
-       implementation_details const& M_implementation_details;
-       typedef typename Allocator::template
-           rebind<qualifier_list<Allocator> >::other qualifier_list_Allocator;
-       qualifier_list_Allocator M_qualifier_list_alloc;
-#if _GLIBCXX_DEMANGLER_CWDEBUG
-       bool M_inside_add_substitution;
-#endif
-
-      public:
-       explicit session(char const* in, int len,
-           implementation_details const& id = implementation_details())
-       : M_str(in), M_pos(0), M_maxpos(len - 1), M_result(true),
-         M_inside_template_args(0), M_inside_type(0),
-         M_inside_substitution(0), M_saw_destructor(false),
-         M_name_is_cdtor(false), M_name_is_template(false),
-         M_name_is_conversion_operator(false),
-         M_template_args_need_space(false), M_template_arg_pos_offset(0),
-         M_implementation_details(id)
-#if _GLIBCXX_DEMANGLER_CWDEBUG
-         , M_inside_add_substitution(false)
-#endif
-       { }
-
-       static int
-       decode_encoding(string_type& output, char const* input, int len,
-         implementation_details const& id = implementation_details());
-
-       bool
-       decode_type(string_type& output,
-                   qualifier_list<Allocator>* qualifiers = NULL)
-       {
-         string_type postfix;
-         bool res = decode_type_with_postfix(output, postfix, qualifiers);
-         output += postfix;
-         return res;
-       }
-
-       bool
-       remaining_input_characters(void) const
-       { return current() != 0; }
-
-      private:
-       char
-       current(void) const
-       { return (M_pos > M_maxpos) ? 0 : M_str[M_pos]; }
-
-       char
-       next_peek(void) const
-       { return (M_pos >= M_maxpos) ? 0 : M_str[M_pos + 1]; }
-
-       char
-       next(void)
-       { return (M_pos >= M_maxpos) ? 0 : M_str[++M_pos]; }
-
-       char
-       eat_current(void)
-       { return (M_pos > M_maxpos) ? 0 : M_str[M_pos++]; }
-
-       void
-       store(int& saved_pos)
-       { saved_pos = M_pos; }
-
-       void
-       restore(int saved_pos)
-       { M_pos = saved_pos; M_result = true; }
-
-       void
-       add_substitution(int start_pos,
-                        substitution_nt sub_type,
-                        int number_of_prefixes);
-
-       bool decode_type_with_postfix(string_type& prefix,
-           string_type& postfix, qualifier_list<Allocator>* qualifiers = NULL);
-       bool decode_bare_function_type(string_type& output);
-       bool decode_builtin_type(string_type& output);
-       bool decode_call_offset(string_type& output);
-       bool decode_class_enum_type(string_type& output);
-       bool decode_expression(string_type& output);
-       bool decode_literal(string_type& output);
-       bool decode_local_name(string_type& output);
-       bool decode_name(string_type& output,
-           string_type& nested_name_qualifiers);
-       bool decode_nested_name(string_type& output,
-           string_type& qualifiers);
-       bool decode_number(string_type& output);
-       bool decode_operator_name(string_type& output);
-       bool decode_source_name(string_type& output);
-       bool decode_substitution(string_type& output,
-           qualifier_list<Allocator>* qualifiers = NULL);
-       bool decode_template_args(string_type& output);
-       bool decode_template_param(string_type& output,
-           qualifier_list<Allocator>* qualifiers = NULL);
-       bool decode_unqualified_name(string_type& output);
-       bool decode_unscoped_name(string_type& output);
-       bool decode_non_negative_decimal_integer(string_type& output);
-       bool decode_special_name(string_type& output);
-        bool decode_real(string_type& output, size_t size_of_real);
-      };
-
-    template<typename Allocator>
-#if !_GLIBCXX_DEMANGLER_CWDEBUG
-      inline
-#endif
-      void
-      session<Allocator>::add_substitution(int start_pos,
-                                          substitution_nt sub_type,
-                                          int number_of_prefixes = 0)
-      {
-       if (!M_inside_substitution)
-       {
-#if _GLIBCXX_DEMANGLER_CWDEBUG
-         if (M_inside_add_substitution)
-           return;
-#endif
-         M_substitutions_pos.
-             push_back(substitution_st(start_pos,
-                 sub_type, number_of_prefixes));
-#if _GLIBCXX_DEMANGLER_CWDEBUG
-         if (!DEBUGCHANNELS::dc::demangler.is_on())
-           return;
-         string_type substitution_name("S");
-         int n = M_substitutions_pos.size() - 1;
-         if (n > 0)
-           substitution_name += (n <= 10) ? (char)(n + '0' - 1)
-                                          : (char)(n + 'A' - 11);
-         substitution_name += '_';
-         string_type subst;
-         int saved_pos = M_pos;
-         M_pos = start_pos;
-         M_inside_add_substitution = true;
-         _GLIBCXX_DEMANGLER_DEBUG( dc::demangler.off() );
-         switch(sub_type)
-         {
-           case type:
-             decode_type(subst);
-             break;
-           case template_template_param:
-             decode_template_param(subst);
-             break;
-           case nested_name_prefix:
-           case nested_name_template_prefix:
-             for (int cnt = number_of_prefixes; cnt > 0; --cnt)
-             {
-               if (current() == 'I')
-               {
-                 subst += ' ';
-                 decode_template_args(subst);
-               }
-               else
-               {
-                 if (cnt < number_of_prefixes)
-                   subst += "::";
-                 if (current() == 'S')
-                   decode_substitution(subst);
-                 else if (current() == 'T')
-                   decode_template_param(subst);
-                 else
-                   decode_unqualified_name(subst);
-               }
-             }
-             break;
-           case unscoped_template_name:
-             decode_unscoped_name(subst);
-             break;
-         }
-         M_pos = saved_pos;
-         _GLIBCXX_DEMANGLER_DEBUG( dc::demangler.on() );
-         _GLIBCXX_DEMANGLER_DOUT(dc::demangler,
-             "Adding substitution " << substitution_name
-             << " : " << subst
-             << " (from " << location_ct((char*)__builtin_return_address(0)
-                                         + builtin_return_address_offset)
-             << " <- " << location_ct((char*)__builtin_return_address(1)
-                                      + builtin_return_address_offset)
-             << " <- " << location_ct((char*)__builtin_return_address(2)
-                                      + builtin_return_address_offset)
-             << ").");
-         M_inside_add_substitution = false;
-#endif
-       }
-      }
-
-    // We don't want to depend on locale (or include <cctype> for that matter).
-    // We also don't want to use "safe-ctype.h" because that headerfile is not
-    // available to the users.
-    inline bool isdigit(char c) { return c >= '0' && c <= '9'; }
-    inline bool islower(char c) { return c >= 'a' && c <= 'z'; }
-    inline bool isupper(char c) { return c >= 'A' && c <= 'Z'; }
-    inline char tolower(char c) { return isupper(c) ? c - 'A' + 'a' : c; }
-
-    //
-    // <non-negative decimal integer> ::= 0
-    //                                ::= 1|2|3|4|5|6|7|8|9 [<digit>+]
-    // <digit>                        ::= 0|1|2|3|4|5|6|7|8|9
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::
-         decode_non_negative_decimal_integer(string_type& output)
-      {
-       char c = current();
-       if (c == '0')
-       {
-         output += '0';
-         eat_current();
-       }
-       else if (!isdigit(c))
-         M_result = false;
-       else
-       {
-         do
-         {
-           output += c;
-         }
-         while (isdigit((c = next())));
-       }
-       return M_result;
-      }
-
-    // <number> ::= [n] <non-negative decimal integer>
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_number(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_number");
-       if (current() != 'n')
-         decode_non_negative_decimal_integer(output);
-       else
-       {
-         output += '-';
-         eat_current();
-         decode_non_negative_decimal_integer(output);
-       }
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <builtin-type> ::= v  # void
-    //                ::= w  # wchar_t
-    //                ::= b  # bool
-    //                ::= c  # char
-    //                ::= a  # signed char
-    //                ::= h  # unsigned char
-    //                ::= s  # short
-    //                ::= t  # unsigned short
-    //                ::= i  # int
-    //                ::= j  # unsigned int
-    //                ::= l  # long
-    //                ::= m  # unsigned long
-    //                ::= x  # long long, __int64
-    //                ::= y  # unsigned long long, __int64
-    //                ::= n  # __int128
-    //                ::= o  # unsigned __int128
-    //                ::= f  # float
-    //                ::= d  # double
-    //                ::= e  # long double, __float80
-    //                ::= g  # __float128
-    //                ::= z  # ellipsis
-    //                ::= u <source-name>    # vendor extended type
-    //
-    char const* const builtin_type_c[26] =
-    {
-      "signed char",   // a
-      "bool",          // b
-      "char",          // c
-      "double",                // d
-      "long double",   // e
-      "float",         // f
-      "__float128",            // g
-      "unsigned char", // h
-      "int",           // i
-      "unsigned int",  // 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
-      "wchar_t",               // w
-      "long long",             // x
-      "unsigned long long",    // y
-      "..."                    // z
-    };
-
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_builtin_type(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_builtin_type");
-       char const* bt;
-       if (!islower(current()) || !(bt = builtin_type_c[current() - 'a']))
-         _GLIBCXX_DEMANGLER_FAILURE;
-       output += bt;
-       eat_current();
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <class-enum-type> ::= <name>
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_class_enum_type(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_class_enum_type");
-       string_type nested_name_qualifiers;
-       if (!decode_name(output, nested_name_qualifiers))
-         _GLIBCXX_DEMANGLER_FAILURE;
-       output += nested_name_qualifiers;
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <substitution> ::=
-    //   S <seq-id> _
-    //   S_
-    //   St # ::std::
-    //   Sa # ::std::allocator
-    //   Sb # ::std::basic_string
-    //   Ss # ::std::basic_string<char, std::char_traits<char>,
-    //                            std::allocator<char> >
-    //   Si # ::std::basic_istream<char,  std::char_traits<char> >
-    //   So # ::std::basic_ostream<char,  std::char_traits<char> >
-    //   Sd # ::std::basic_iostream<char, std::char_traits<char> >
-    //
-    // <seq-id> ::=
-    //   0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z
-    //       [<seq-id>]        # Base 36 number
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_substitution(string_type& output,
-         qualifier_list<Allocator>* qualifiers)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_substitution");
-       unsigned int value = 0;
-       char c = next();
-       if (c != '_')
-       {
-         switch(c)
-         {
-           case 'a':
-           {
-             output += "std::allocator";
-             if (!M_inside_template_args)
-             {
-               M_function_name = "allocator";
-               M_name_is_template = true;
-               M_name_is_cdtor = false;
-               M_name_is_conversion_operator = false;
-             }
-             eat_current();
-             if (qualifiers)
-               qualifiers->printing_suppressed();
-             _GLIBCXX_DEMANGLER_RETURN;
-           }
-           case 'b':
-           {
-             output += "std::basic_string";
-             if (!M_inside_template_args)
-             {
-               M_function_name = "basic_string";
-               M_name_is_template = true;
-               M_name_is_cdtor = false;
-               M_name_is_conversion_operator = false;
-             }
-             eat_current();
-             if (qualifiers)
-               qualifiers->printing_suppressed();
-             _GLIBCXX_DEMANGLER_RETURN;
-           }
-           case 'd':
-             output += "std::iostream";
-             if (!M_inside_template_args)
-             {
-               M_function_name = "iostream";
-               M_name_is_template = true;
-               M_name_is_cdtor = false;
-               M_name_is_conversion_operator = false;
-             }
-             eat_current();
-             if (qualifiers)
-               qualifiers->printing_suppressed();
-             _GLIBCXX_DEMANGLER_RETURN;
-           case 'i':
-             output += "std::istream";
-             if (!M_inside_template_args)
-             {
-               M_function_name = "istream";
-               M_name_is_template = true;
-               M_name_is_cdtor = false;
-               M_name_is_conversion_operator = false;
-             }
-             eat_current();
-             if (qualifiers)
-               qualifiers->printing_suppressed();
-             _GLIBCXX_DEMANGLER_RETURN;
-           case 'o':
-             output += "std::ostream";
-             if (!M_inside_template_args)
-             {
-               M_function_name = "ostream";
-               M_name_is_template = true;
-               M_name_is_cdtor = false;
-               M_name_is_conversion_operator = false;
-             }
-             eat_current();
-             if (qualifiers)
-               qualifiers->printing_suppressed();
-             _GLIBCXX_DEMANGLER_RETURN;
-           case 's':
-             output += "std::string";
-             if (!M_inside_template_args)
-             {
-               M_function_name = "string";
-               M_name_is_template = true;
-               M_name_is_cdtor = false;
-               M_name_is_conversion_operator = false;
-             }
-             eat_current();
-             if (qualifiers)
-               qualifiers->printing_suppressed();
-             _GLIBCXX_DEMANGLER_RETURN;
-           case 't':
-             output += "std";
-             eat_current();
-             if (qualifiers)
-               qualifiers->printing_suppressed();
-             _GLIBCXX_DEMANGLER_RETURN;
-           default:
-             for(;; c = next())
-             {
-               if (isdigit(c))
-                 value = value * 36 + c - '0';
-               else if (isupper(c))
-                 value = value * 36 + c - 'A' + 10;
-               else if (c == '_')
-                 break;
-               else
-                 _GLIBCXX_DEMANGLER_FAILURE;
-             }
-             ++value;
-             break;
-         }
-       }
-       eat_current();
-       if (value >= M_substitutions_pos.size() ||
-           M_inside_type > 20)                 // Rather than core dump.
-         _GLIBCXX_DEMANGLER_FAILURE;
-       ++M_inside_substitution;
-       int saved_pos = M_pos;
-       substitution_st& substitution(M_substitutions_pos[value]);
-       M_pos = substitution.M_start_pos;
-       switch(substitution.M_type)
-       {
-         case type:
-           decode_type(output, qualifiers);
-           break;
-         case template_template_param:
-           decode_template_param(output, qualifiers);
-           break;
-         case nested_name_prefix:
-         case nested_name_template_prefix:
-           for (int cnt = substitution.M_number_of_prefixes; cnt > 0; --cnt)
-           {
-             if (current() == 'I')
-             {
-               if (M_template_args_need_space)
-                 output += ' ';
-               M_template_args_need_space = false;
-               if (!decode_template_args(output))
-                 _GLIBCXX_DEMANGLER_FAILURE;
-             }
-             else
-             {
-               if (cnt < substitution.M_number_of_prefixes)
-                 output += "::";
-               if (current() == 'S')
-               {
-                 if (!decode_substitution(output))
-                   _GLIBCXX_DEMANGLER_FAILURE;
-               }
-               else if (!decode_unqualified_name(output))
-                 _GLIBCXX_DEMANGLER_FAILURE;
-             }
-           }
-           if (qualifiers)
-             qualifiers->printing_suppressed();
-           break;
-         case unscoped_template_name:
-           decode_unscoped_name(output);
-           if (qualifiers)
-             qualifiers->printing_suppressed();
-           break;
-       }
-       M_pos = saved_pos;
-       --M_inside_substitution;
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <template-param> ::= T_                 # first template parameter
-    //                  ::= T <parameter-2 non-negative number> _
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_template_param(string_type& output,
-         qualifier_list<Allocator>* qualifiers)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_template_parameter");
-       if (current() != 'T')
-         _GLIBCXX_DEMANGLER_FAILURE;
-       unsigned int value = 0;
-       char c;
-       if ((c = next()) != '_')
-       {
-         while(isdigit(c))
-         {
-           value = value * 10 + c - '0';
-           c = next();
-         }
-         ++value;
-       }
-       if (eat_current() != '_')
-         _GLIBCXX_DEMANGLER_FAILURE;
-       value += M_template_arg_pos_offset;
-       if (value >= M_template_arg_pos.size())
-         _GLIBCXX_DEMANGLER_FAILURE;
-       int saved_pos = M_pos;
-       M_pos = M_template_arg_pos[value];
-       if (M_inside_type > 20)         // Rather than core dump.
-         _GLIBCXX_DEMANGLER_FAILURE;
-       ++M_inside_substitution;
-       if (current() == 'X')
-       {
-         eat_current();
-         decode_expression(output);
-       }
-       else if (current() == 'L')
-         decode_literal(output);
-       else
-         decode_type(output, qualifiers);
-       --M_inside_substitution;
-       M_pos = saved_pos;
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_real(string_type& output, size_t size_of_real)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_real");
-
-       unsigned long words[4]; // 32 bit per long, maximum of 128 bits.
-       unsigned long* word = &words[0];
-
-       int saved_pos;
-       store(saved_pos);
-
-       // The following assumes that leading zeroes are also included in the
-       // mangled name, I am not sure that is conforming to the C++-ABI, but
-       // it is what g++ does.
-       unsigned char nibble, c = current();
-       for(size_t word_cnt = size_of_real / 4; word_cnt > 0; --word_cnt)
-       {
-         for (int nibble_cnt = 0; nibble_cnt < 8; ++nibble_cnt)
-         {
-           // Translate character into nibble.
-           if (c < '0' || c > 'f')
-             _GLIBCXX_DEMANGLER_FAILURE;
-           if (c <= '9')
-             nibble = c - '0';
-           else if (c >= 'a')
-             nibble = c - 'a' + 10;
-           else
-             _GLIBCXX_DEMANGLER_FAILURE;
-           // Write nibble into word array.
-           if (nibble_cnt == 0)
-             *word = nibble << 28;
-           else
-             *word |= (nibble << (28 - 4 * nibble_cnt));
-           c = next();
-         }
-         ++word;
-       }
-       char buf[24];
-       if (M_implementation_details.decode_real(buf, words, size_of_real))
-       {
-         output += buf;
-         _GLIBCXX_DEMANGLER_RETURN;
-       }
-       restore(saved_pos);
-
-       output += '[';
-       c = current();
-       for(size_t nibble_cnt = 0; nibble_cnt < 2 * size_of_real; ++nibble_cnt)
-       {
-         if (c < '0' || c > 'f' || (c > '9' && c < 'a'))
-           _GLIBCXX_DEMANGLER_FAILURE;
-         output += c;
-         c = next();
-       }
-       output += ']';
-
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_literal(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_literal");
-       eat_current();  // Eat the 'L'.
-       if (current() == '_')
-       {
-         if (next() != 'Z')
-           _GLIBCXX_DEMANGLER_FAILURE;
-         eat_current();
-         if ((M_pos += decode_encoding(output, M_str + M_pos,
-                 M_maxpos - M_pos + 1, M_implementation_details)) < 0)
-           _GLIBCXX_DEMANGLER_FAILURE;
-       }
-       else
-       {
-         // Special cases
-         if (current() == 'b')
-         {
-           if (next() == '0')
-             output += "false";
-           else
-             output += "true";
-           eat_current();
-           _GLIBCXX_DEMANGLER_RETURN;
-         }
-         char c = current();
-         if ((c == 'i' || c == 'j' || c == 'l' ||
-              c == 'm' || c == 'x' || c == 'y') &&
-              M_implementation_details.get_style_literal())
-           eat_current();
-         else if (c == 'i' &&
-             !M_implementation_details.get_style_literal_int())
-           eat_current();
-         else
-         {
-           output += '(';
-           if (!decode_type(output))
-             _GLIBCXX_DEMANGLER_FAILURE;
-           output += ')';
-         }
-         if (c >= 'd' && c <= 'g')
-         {
-           size_t size_of_real = (c == 'd') ? sizeof(double) :
-               ((c == 'f') ? sizeof(float) :
-               (c == 'e') ?  sizeof(long double) : 16);
-           if (!decode_real(output, size_of_real))
-               _GLIBCXX_DEMANGLER_FAILURE;
-         }
-         else if (!decode_number(output))
-           _GLIBCXX_DEMANGLER_FAILURE;
-          if (M_implementation_details.get_style_literal())
-         {
-           if (c == 'j' || c == 'm' || c == 'y')
-             output += 'u';
-           if (c == 'l' || c == 'm')
-             output += 'l';
-           if (c == 'x' || c == 'y')
-             output += "ll";
-         }
-       }
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <operator-name> ::=
-    //   nw                            # new
-    //   na                            # new[]
-    //   dl                            # delete
-    //   da                            # delete[]
-    //   ps                            # + (unary)
-    //   ng                            # - (unary)
-    //   ad                            # & (unary)
-    //   de                            # * (unary)
-    //   co                            # ~
-    //   pl                            # +
-    //   mi                            # -
-    //   ml                            # *
-    //   dv                            # /
-    //   rm                            # %
-    //   an                            # &
-    //   or                            # |
-    //   eo                            # ^
-    //   aS                            # =
-    //   pL                            # +=
-    //   mI                            # -=
-    //   mL                            # *=
-    //   dV                            # /=
-    //   rM                            # %=
-    //   aN                            # &=
-    //   oR                            # |=
-    //   eO                            # ^=
-    //   ls                            # <<
-    //   rs                            # >>
-    //   lS                            # <<=
-    //   rS                            # >>=
-    //   eq                            # ==
-    //   ne                            # !=
-    //   lt                            # <
-    //   gt                            # >
-    //   le                            # <=
-    //   ge                            # >=
-    //   nt                            # !
-    //   aa                            # &&
-    //   oo                            # ||
-    //   pp                            # ++
-    //   mm                            # --
-    //   cm                            # ,
-    //   pm                            # ->*
-    //   pt                            # ->
-    //   cl                            # ()
-    //   ix                            # []
-    //   qu                            # ?
-    //   st                            # sizeof (a type)
-    //   sz                            # sizeof (an expression)
-    //   cv <type>                     # (cast)
-    //   v <digit> <source-name>       # vendor extended operator
-    //
-    // Symbol operator codes exist of two characters, we need to find a
-    // quick hash so that their names can be looked up in a table.
-    //
-    // The puzzle :)
-    // Shift the rows so that there is at most one character per column.
-    //
-    // A perfect solution (Oh no, it's THE MATRIX!):
-    //                                              horizontal
-    //    .......................................   offset + 'a'
-    // a, a||d|||||||||n||||s||||||||||||||||||||       0
-    // c,  || |||||||lm o||| ||||||||||||||||||||       0
-    // d,  || a|||e||    l|| ||||||v|||||||||||||       4
-    // e,  ||  ||| ||     || |||o|q |||||||||||||       8
-    // g,  ||  ||| ||     || e|| |  ||||||||t||||      15
-    // i,  ||  ||| ||     ||  || |  |||||||| |||x      15
-    // l,  |e  ||| ||     st  || |  |||||||| |||       -2
-    // m,  |   |i| lm         || |  |||||||| |||       -2
-    // n,  a   e g            t| w  |||||||| |||        1
-    // o,                      |    ||||o||r |||       16
-    // p,                      |    ||lm |p  st|       17
-    // q,                      |    u|   |     |        6
-    // r,                      m     s   |     |        9
-    // s,                                t     z       12
-    //    .......................................
-    // ^            ^__ second character
-    // |___ first character
-    //
-
-    // Putting that solution in tables:
-
-    char const offset_table_c [1 + CHAR_MAX - CHAR_MIN ] =
-    {
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-#if (CHAR_MIN < 0)
-      // Add -CHAR_MIN extra zeroes (128):
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      //   a    b    c    d    e    f    g    h    i    j    k
-      0, -97,   0, -97, -93, -89,   0, -82,   0, -82,   0,   0,
-      //   l    m    n    o    p    q    r    s    t    u    v
-        -99, -99, -96, -81, -80, -91, -88, -85,   0,   0,   0,
-#else
-      //   a    b    c    d    e    f    g    h    i    j    k
-      0, 159,   0, 159, 163, 167,   0, 174,   0, 174,   0,   0,
-      //   l    m    n    o    p    q    r    s    t    u    v
-        157, 157, 160, 175, 176, 165, 168, 171,   0,   0,   0,
-#endif
-      // ... more zeros
-    };
-
-    enum xary_nt {
-      unary,
-      binary,
-      trinary
-    };
-
-    struct entry_st
-    {
-      char const* opcode;
-      char const* symbol_name;
-      xary_nt type;
-    };
-
-    entry_st const symbol_name_table_c[39] = {
-      { "aa",  "operator&&", binary },
-      { "na",  "operator new[]", unary },
-      { "le",  "operator<=", binary },
-      { "ad",  "operator&", unary },
-      { "da",  "operator delete[]", unary },
-      { "ne",  "operator!=", binary },
-      { "mi=", "operator-", binary },
-      { "ng",  "operator-", unary },
-      { "de",  "operator*", unary },
-      { "ml=", "operator*", binary },
-      { "mm",  "operator--", unary },
-      { "cl",  "operator()", unary },
-      { "cm",  "operator,", binary },
-      { "an=", "operator&", binary },
-      { "co",  "operator~", binary },
-      { "dl",  "operator delete", unary },
-      { "ls=", "operator<<", binary },
-      { "lt",  "operator<", binary },
-      { "as=", "operator", binary },
-      { "ge",  "operator>=", binary },
-      { "nt",  "operator!", unary },
-      { "rm=", "operator%", binary },
-      { "eo=", "operator^", binary },
-      { "nw",  "operator new", unary },
-      { "eq",  "operator==", binary },
-      { "dv=", "operator/", binary },
-      { "qu",  "operator?", trinary },
-      { "rs=", "operator>>", binary },
-      { "pl=", "operator+", binary },
-      { "pm",  "operator->*", binary },
-      { "oo",  "operator||", binary },
-      { "st",  "sizeof", unary },
-      { "pp",  "operator++", unary },
-      { "or=", "operator|", binary },
-      { "gt",  "operator>", binary },
-      { "ps",  "operator+", unary },
-      { "pt",  "operator->", binary },
-      { "sz",  "sizeof", unary },
-      { "ix",  "operator[]", unary }
-    };
-
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_operator_name(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_operator_name");
-
-       char opcode0 = current();
-       char opcode1 = tolower(next());
-
-       register char hash;
-       if ((hash = offset_table_c[opcode0 - CHAR_MIN]))
-       {
-         hash += opcode1;
-         if (
-#if (CHAR_MIN < 0)
-             hash >= 0 &&
-#endif
-             hash < 39)
-         {
-           int index = static_cast<int>(static_cast<unsigned char>(hash));
-           entry_st entry = symbol_name_table_c[index];
-           if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1
-               && (opcode1 == current() || entry.opcode[2] == '='))
-           {
-             output += entry.symbol_name;
-             if (opcode1 != current())
-               output += '=';
-             eat_current();
-             if (hash == 16 || hash == 17)
-               M_template_args_need_space = true;
-             _GLIBCXX_DEMANGLER_RETURN;
-           }
-           else if (opcode0 == 'c' && opcode1 == 'v')  // casting operator
-           {
-             eat_current();
-             output += "operator ";
-             if (current() == 'T')
-             {
-               // This is a templated cast operator.
-               // It must be of the form "cvT_I...E".
-               // Let M_template_arg_pos already point
-               // to the template argument.
-               M_template_arg_pos_offset = M_template_arg_pos.size();
-               M_template_arg_pos.push_back(M_pos + 3);
-             }
-             if (!decode_type(output))
-               _GLIBCXX_DEMANGLER_FAILURE;
-             if (!M_inside_template_args)
-               M_name_is_conversion_operator = true;
-             _GLIBCXX_DEMANGLER_RETURN;
-           }
-         }
-       }
-       _GLIBCXX_DEMANGLER_FAILURE;
-      }
-
-    //
-    // <expression> ::= <unary operator-name> <expression>
-    //              ::= <binary operator-name> <expression> <expression>
-    //              ::= <trinary operator-name> <expression> <expression> <expression>
-    //              ::= st <type>
-    //              ::= <template-param>
-    //              ::= sr <type> <unqualified-name>                   # dependent name
-    //              ::= sr <type> <unqualified-name> <template-args>   # dependent template-id
-    //              ::= <expr-primary>
-    //
-    // <expr-primary> ::= L <type> <value number> E     # integer literal
-    //                ::= L <type> <value float> E     # floating literal
-    //                ::= L <mangled-name> E           # external name
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_expression(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_expression");
-       if (current() == 'T')
-       {
-         if (!decode_template_param(output))
-           _GLIBCXX_DEMANGLER_FAILURE;
-         _GLIBCXX_DEMANGLER_RETURN;
-       }
-       else if (current() == 'L')
-       {
-         if (!decode_literal(output))
-           _GLIBCXX_DEMANGLER_FAILURE;
-         if (current() != 'E')
-           _GLIBCXX_DEMANGLER_FAILURE;
-         eat_current();
-         _GLIBCXX_DEMANGLER_RETURN;
-       }
-       else if (current() == 's')
-       {
-         char opcode1 = next();
-         if (opcode1 == 't' || opcode1 == 'z')
-         {
-           eat_current();
-           if (M_implementation_details.get_style_compact_expr_ops())
-             output += "sizeof(";
-           else
-             output += "sizeof (";
-           if (opcode1 == 't')
-           {
-             // I cannot think of a mangled name that is valid for both cases
-             // when just replacing the 't' by a 'z' or vica versa, which
-             // indicates that there is no ambiguity that dictates the need
-             // for a seperate "st" case, except to be able catch invalid
-             // mangled names.  However there CAN be ambiguity in the demangled
-             // name when there are both a type and a symbol of the same name,
-             // which then leads to different encoding (of course) with
-             // sizeof (type) or sizeof (expression) respectively, but that
-             // ambiguity is not per se related to "sizeof" except that that
-             // is the only place where both a type AND an expression are valid
-             // in as part of a (template function) type.
-             //
-             // Example:
-             //
-             // struct B { typedef int t; };
-             // struct A : public B { static int t[2]; };
-             // template<int i, int j> struct C { typedef int q; };
-             // template<int i, typename T>
-             //   void f(typename C<sizeof (typename T::t),
-             //                     sizeof (T::t)>::q) { }
-             // void instantiate() { f<5, A>(0); }
-             //
-             // Leads to _Z1fILi5E1AEvN1CIXstN1T1tEEXszsrS2_1tEE1qE which
-             // demangles as
-             // void f<5, A>(C<sizeof (T::t), sizeof (T::t)>::q)
-             //
-             // This is ambiguity is very unlikely to happen and it is kind
-             // of fuzzy to detect when adding a 'typename' makes sense.
-             //
-             if (M_implementation_details.get_style_sizeof_typename())
-             {
-               // We can only get here inside a template parameter,
-               // so this is syntactically correct if the given type is
-               // a typedef.  The only disadvantage is that it is inconsistent
-               // with all other places where the 'typename' keyword should be
-               // used and we don't.
-               // With this, the above example will demangle as
-               // void f<5, A>(C<sizeof (typename T::t), sizeof (T::t)>::q)
-               if (current() == 'N' || // <nested-name>
-                                         // This should be a safe bet.
-                   (current() == 'S' &&
-                    next_peek() == 't'))       // std::something, guess that
-                                         // this involves a typedef.
-                 output += "typename ";
-             }
-             if (!decode_type(output))
-               _GLIBCXX_DEMANGLER_FAILURE;
-           }
-           else
-           {
-             if (!decode_expression(output))
-               _GLIBCXX_DEMANGLER_FAILURE;
-           }
-           output += ')';
-           _GLIBCXX_DEMANGLER_RETURN;
-         }
-         else if (current() == 'r')
-         {
-           eat_current();
-           if (!decode_type(output))
-             _GLIBCXX_DEMANGLER_FAILURE;
-           output += "::";
-           if (!decode_unqualified_name(output))
-             _GLIBCXX_DEMANGLER_FAILURE;
-           if (current() != 'I' || decode_template_args(output))
-             _GLIBCXX_DEMANGLER_RETURN;
-         }
-       }
-       else
-       {
-         char opcode0 = current();
-         char opcode1 = tolower(next());
-
-         register char hash;
-         if ((hash = offset_table_c[opcode0 - CHAR_MIN]))
-         {
-           hash += opcode1;
-           if (
-#if (CHAR_MIN < 0)
-               hash >= 0 &&
-#endif
-               hash < 39)
-           {
-             int index = static_cast<int>(static_cast<unsigned char>(hash));
-             entry_st entry = symbol_name_table_c[index];
-             if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1
-                 && (opcode1 == current() || entry.opcode[2] == '='))
-             {
-               char const* op = entry.symbol_name + 8; // Skip "operator".
-               if (*op == ' ')                         // operator new and delete.
-                 ++op;
-               if (entry.type == unary)
-                 output += op;
-               bool is_eq = (opcode1 != current());
-               eat_current();
-               if (index == 34 && M_inside_template_args)      // operator>
-                 output += '(';
-               output += '(';
-               if (!decode_expression(output))
-                 _GLIBCXX_DEMANGLER_FAILURE;
-               output += ')';
-               if (entry.type != unary)
-               {
-                 if (!M_implementation_details.get_style_compact_expr_ops())
-                   output += ' ';
-                 output += op;
-                 if (is_eq)
-                   output += '=';
-                 if (!M_implementation_details.get_style_compact_expr_ops())
-                   output += ' ';
-                 output += '(';
-                 if (!decode_expression(output))
-                   _GLIBCXX_DEMANGLER_FAILURE;
-                 output += ')';
-                 if (index == 34 && M_inside_template_args)
-                   output += ')';
-                 if (entry.type == trinary)
-                 {
-                   if (M_implementation_details.get_style_compact_expr_ops())
-                     output += ":(";
-                   else
-                     output += " : (";
-                   if (!decode_expression(output))
-                     _GLIBCXX_DEMANGLER_FAILURE;
-                   output += ')';
-                 }
-               }
-               _GLIBCXX_DEMANGLER_RETURN;
-             }
-             else if (opcode0 == 'c' &&
-                      opcode1 == 'v')          // casting operator.
-             {
-               eat_current();
-               output += '(';
-               if (!decode_type(output))
-                 _GLIBCXX_DEMANGLER_FAILURE;
-               output += ")(";
-               if (!decode_expression(output))
-                 _GLIBCXX_DEMANGLER_FAILURE;
-               output += ')';
-               _GLIBCXX_DEMANGLER_RETURN;
-             }
-           }
-         }
-       }
-       _GLIBCXX_DEMANGLER_FAILURE;
-      }
-
-    //
-    // <template-args> ::= I <template-arg>+ E
-    // <template-arg> ::= <type>                       # type or template
-    //                ::= L <type> <value number> E    # integer literal
-    //                ::= L <type> <value float> E     # floating literal
-    //                ::= L <mangled-name> E           # external name
-    //                ::= X <expression> E             # expression
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_template_args(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_template_args");
-       if (eat_current() != 'I')
-         _GLIBCXX_DEMANGLER_FAILURE;
-       int prev_size = M_template_arg_pos.size();
-       ++M_inside_template_args;
-       if (M_template_args_need_space)
-       {
-         output += ' ';
-         M_template_args_need_space = false;
-       }
-       output += '<';
-       for(;;)
-       {
-         if (M_inside_template_args == 1 && !M_inside_type)
-           M_template_arg_pos.push_back(M_pos);
-         if (current() == 'X')
-         {
-           eat_current();
-           if (!decode_expression(output))
-             _GLIBCXX_DEMANGLER_FAILURE;
-           if (current() != 'E')
-             _GLIBCXX_DEMANGLER_FAILURE;
-           eat_current();
-         }
-         else if (current() == 'L')
-         {
-           if (!decode_literal(output))
-             _GLIBCXX_DEMANGLER_FAILURE;
-           if (current() != 'E')
-             _GLIBCXX_DEMANGLER_FAILURE;
-           eat_current();
-         }
-         else if (!decode_type(output))
-           _GLIBCXX_DEMANGLER_FAILURE;
-         if (current() == 'E')
-           break;
-         output += ", ";
-       }
-       eat_current();
-       if (*(output.rbegin()) == '>')
-         output += ' ';
-       output += '>';
-       --M_inside_template_args;
-       if (!M_inside_template_args && !M_inside_type)
-       {
-         M_name_is_template = true;
-         M_template_arg_pos_offset = prev_size;
-       }
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <bare-function-type> ::=
-    //   <signature type>+             # Types are parameter types.
-    //
-    // Note that the possible return type of the <bare-function-type>
-    // has already been eaten before we call this function.  This makes
-    // our <bare-function-type> slightly different from the one in
-    // the C++-ABI description.
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_bare_function_type(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_bare_function_type");
-       if (M_saw_destructor)
-       {
-         if (eat_current() != 'v' || (current() != 'E' && current() != 0))
-           _GLIBCXX_DEMANGLER_FAILURE;
-         output += "()";
-         M_saw_destructor = false;
-         _GLIBCXX_DEMANGLER_RETURN;
-       }
-       if (current() == 'v' && !M_implementation_details.get_style_void())
-       {
-         eat_current();
-         if (current() != 'E' && current() != 0)
-           _GLIBCXX_DEMANGLER_FAILURE;
-         output += "()";
-         M_saw_destructor = false;
-         _GLIBCXX_DEMANGLER_RETURN;
-       }
-       output += '(';
-       M_template_args_need_space = false;
-       if (!decode_type(output))       // Must have at least one parameter.
-         _GLIBCXX_DEMANGLER_FAILURE;
-       while (current() != 'E' && current() != 0)
-       {
-         output += ", ";
-         if (!decode_type(output))
-           _GLIBCXX_DEMANGLER_FAILURE;
-       }
-       output += ')';
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <type> ::=
-    //   <builtin-type>                # Starts with a lower case character != r.
-    //   <function-type>       # Starts with F
-    //   <class-enum-type>     # Starts with N, S, C, D, Z, a digit or a lower
-    //                         # case character.  Since a lower case character
-    //                         # would be an operator name, that would be an
-    //                         # error.  The S is a substitution or St
-    //                         # (::std::).  A 'C' would be a constructor and
-    //                         # thus also an error.
-    //   <template-param>      # Starts with T
-    //   <substitution>         # Starts with S
-    //   <template-template-param> <template-args>  # Starts with T or S,
-    //                                             # equivalent with the above.
-    //
-    //   <array-type>                  # Starts with A
-    //   <pointer-to-member-type>      # Starts with M
-    //   <CV-qualifiers> <type>                # Starts with r, V or K
-    //   P <type>   # pointer-to       # Starts with P
-    //   R <type>   # reference-to     # Starts with R
-    //   C <type>   # complex (C 2000) # Starts with C
-    //   G <type>   # imaginary (C 2000)# Starts with G
-    //   U <source-name> <type>                # vendor extended type qualifier,
-    //                                 # starts with U
-    //
-    // <template-template-param> ::= <template-param>
-    //                           ::= <substitution>
-
-    // My own analysis of how to decode qualifiers:
-    //
-    // F is a <function-type>, <T> is a <builtin-type>, <class-enum-type>,
-    //   <template-param> or <template-template-param> <template-args>.
-    // <Q> represents a series of qualifiers (not G or C).
-    // <C> is an unqualified type.
-    // <R> is a qualified type.
-    // <B> is the bare-function-type without return type.
-    // <I> is the array index.
-    //                                         Substitutions:
-    // <Q>M<C><Q2>F<R><B>E  ==> R (C::*Q)B Q2  "<C>", "F<R><B>E"
-    //                                             (<R> and <B> recursive),
-    //                                             "M<C><Q2>F<R><B>E".
-    // <Q>F<R><B>E         ==> R (Q)B          "<R>", "<B>" (<B> recursive)
-    //                                              and "F<R><B>E".
-    //
-    // Note that if <R> has postfix qualifiers (an array or function), then
-    // those are added AFTER the (member) function type.  For example:
-    // <Q>FPA<R><B>E ==> R (*(Q)B) [], where the PA added the prefix
-    // "(*" and the postfix ") []".
-    //
-    // <Q>G<T>             ==> imaginary T Q   "<T>", "G<T>" (<T> recursive).
-    // <Q>C<T>             ==> complex T Q     "<T>", "C<T>" (<T> recursive).
-    // <Q><T>              ==> T Q             "<T>" (<T> recursive).
-    //
-    // where <Q> is any of:
-    //
-    // <Q>P            ==> *Q                          "P..."
-    // <Q>R            ==> &Q                          "R..."
-    // <Q>[K|V|r]+     ==> [ const| volatile| restrict]+Q      "KVr..."
-    // <Q>U<S>         ==>  SQ                         "U<S>..."
-    // <Q>M<C>         ==> C::*Q                       "M<C>..." (<C> recurs.)
-    // A<I>            ==>  [I]                        "A<I>..." (<I> recurs.)
-    // <Q>A<I>         ==>  (Q) [I]                    "A<I>..." (<I> recurs.)
-    //   Note that when <Q> ends on an A<I2> then the brackets are omitted
-    //   and no space is written between the two:
-    //   A<I2>A<I>     ==>  [I2][I]
-    //   If <Q> ends on [KVr]+, which can happen in combination with
-    //   substitutions only, then special handling is required, see below.
-    //
-    // A <substitution> is handled with an input position switch during which
-    // new substitutions are turned off.  Because recursive handling of types
-    // (and therefore the order in which substitutions must be generated) must
-    // be done left to right, but the generation of Q needs processing right to
-    // left, substitutions per <type> are generated by reading the input left
-    // to right and marking the starts of all substitutions only - implicitly
-    // finishing them at the end of the type.  Then the output and real
-    // substitutions are generated.
-    //
-    // The following comment was for the demangling of g++ version 3.0.x.  The
-    // mangling (and I believe even the ABI description) have been fixed now
-    // (as of g++ version 3.1).
-    //
-    // g++ 3.0.x only:
-    // The ABI specifies for pointer-to-member function types the format
-    // <Q>M<T>F<R><B>E.  In other words, the qualifier <Q2> (see above) is
-    // implicitely contained in <T> instead of explicitly part of the M format.
-    // I am convinced that this is a bug in the ABI.  Unfortunately, this is
-    // how we have to demangle things as it has a direct impact on the order
-    // in which substitutions are stored.  This ill-formed design results in
-    // rather ill-formed demangler code too however :/
-    //
-    // <Q2> is now explicitely part of the M format.
-    // For some weird reason, g++ (3.2.1) does not add substitutions for
-    // qualified member function pointers.  I think that is another bug.
-    //
-
-    // In the case of
-    // <Q>A<I>
-    // where <Q> ends on [K|V|r]+ then that part should be processed as
-    // if it was behind the A<I> instead of in front of it.  This is
-    // because a constant array of ints is normally always mangled as
-    // an array of constant ints.  KVr qualifiers can end up in front
-    // of an array when the array is part of a substitution or template
-    // parameter, but the demangling should still result in the same
-    // syntax; thus KA2_i (const array of ints) must result in the same
-    // demangling as A2_Ki (array of const ints).  As a result we must
-    // demangle ...[...[[KVr]+A<I0>][KVr]+A<I1>]...[KVr]+A<In>[KVr]+
-    // as A<I0>A<I1>...A<In>[KVr]+ where each K, V and r in the series
-    // collapses to a single character at the right of the string.
-    // For example:
-    // VA9_KrA6_KVi --> A9_A6_KVri --> int volatile const restrict [9][6]
-    // Note that substitutions are still added as usual (the translation
-    // to A9_A6_KVri does not really happen).
-    //
-    // This decoding is achieved by delaying the decoding of any sequence
-    // of [KVrA]'s and processing them together in the order: first the
-    // short-circuited KVr part and then the arrays.
-    static int const cvq_K = 1;                // Saw at least one K
-    static int const cvq_V = 2;                // Saw at least one V
-    static int const cvq_r = 4;                // Saw at least one r
-    static int const cvq_A = 8;                // Saw at least one A
-    static int const cvq_last = 16;    // No remaining qualifiers.
-    static int const cvq_A_cnt = 32;   // Bit 5 and higher represent the
-                                       //   number of A's in the series.
-    // In the function below, iter_array points to the first (right most)
-    // A in the series, if any.
-    template<typename Allocator>
-      void
-      qualifier_list<Allocator>::decode_KVrA(
-          string_type& prefix, string_type& postfix, int cvq,
-          typename qual_vector::const_reverse_iterator const& iter_array) const
-       {
-         _GLIBCXX_DEMANGLER_DOUT_ENTERING3("decode_KVrA");
-         if ((cvq & cvq_K))
-           prefix += " const";
-         if ((cvq & cvq_V))
-           prefix += " volatile";
-         if ((cvq & cvq_r))
-           prefix += " restrict";
-         if ((cvq & cvq_A))
-         {
-           int n = cvq >> 5;
-           for (typename qual_vector::
-               const_reverse_iterator iter = iter_array;
-               iter != M_qualifier_starts.rend(); ++iter)
-           {
-             switch((*iter).first_qualifier())
-             {
-               case 'K':
-               case 'V':
-               case 'r':
-                 break;
-               case 'A':
-               {
-                 string_type index = (*iter).get_optional_type();
-                 if (--n == 0 && (cvq & cvq_last))
-                   postfix = " [" + index + "]" + postfix;
-                 else if (n > 0)
-                   postfix = "[" + index + "]" + postfix;
-                 else
-                 {
-                   prefix += " (";
-                   postfix = ") [" + index + "]" + postfix;
-                 }
-                 break;
-               }
-               default:
-                 _GLIBCXX_DEMANGLER_RETURN3;
-             }
-           }
-         }
-         _GLIBCXX_DEMANGLER_RETURN3;
-       }
-
-    template<typename Allocator>
-      void
-      qualifier_list<Allocator>::decode_qualifiers(
-         string_type& prefix,
-         string_type& postfix,
-         bool member_function_pointer_qualifiers = false) const
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING3("decode_qualifiers");
-       int cvq = 0;
-       typename qual_vector::const_reverse_iterator iter_array;
-       for(typename qual_vector::
-           const_reverse_iterator iter = M_qualifier_starts.rbegin();
-           iter != M_qualifier_starts.rend(); ++iter)
-       {
-         if (!member_function_pointer_qualifiers
-             && !(*iter).part_of_substitution())
-         {
-           int saved_inside_substitution = M_demangler.M_inside_substitution;
-           M_demangler.M_inside_substitution = 0;
-           M_demangler.add_substitution((*iter).get_start_pos(), type);
-           M_demangler.M_inside_substitution = saved_inside_substitution;
-         }
-         char qualifier_char = (*iter).first_qualifier();
-         for(; qualifier_char; qualifier_char = (*iter).next_qualifier())
-         {
-           switch(qualifier_char)
-           {
-             case 'P':
-               if (cvq)
-               {
-                 decode_KVrA(prefix, postfix, cvq, iter_array);
-                 cvq = 0;
-               }
-               prefix += "*";
-               break;
-             case 'R':
-               if (cvq)
-               {
-                 decode_KVrA(prefix, postfix, cvq, iter_array);
-                 cvq = 0;
-               }
-               prefix += "&";
-               break;
-             case 'K':
-               cvq |= cvq_K;
-               continue;
-             case 'V':
-               cvq |= cvq_V;
-               continue;
-             case 'r':
-               cvq |= cvq_r;
-               continue;
-             case 'A':
-               if (!(cvq & cvq_A))
-               {
-                 cvq |= cvq_A;
-                 iter_array = iter;
-               }
-               cvq += cvq_A_cnt;
-               break;
-             case 'M':
-               if (cvq)
-               {
-                 decode_KVrA(prefix, postfix, cvq, iter_array);
-                 cvq = 0;
-               }
-               prefix += " ";
-               prefix += (*iter).get_optional_type();
-               prefix += "::*";
-               break;
-             case 'U':
-               if (cvq)
-               {
-                 decode_KVrA(prefix, postfix, cvq, iter_array);
-                 cvq = 0;
-               }
-               prefix += " ";
-               prefix += (*iter).get_optional_type();
-               break;
-             case 'G': // Only here so we added a substitution.
-               break;
-           }
-           break;
-         }
-       }
-       if (cvq)
-         decode_KVrA(prefix, postfix, cvq|cvq_last, iter_array);
-       M_printing_suppressed = false;
-       _GLIBCXX_DEMANGLER_RETURN3;
-      }
-
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_type_with_postfix(
-         string_type& prefix, string_type& postfix,
-         qualifier_list<Allocator>* qualifiers)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING2("decode_type");
-       ++M_inside_type;
-       bool recursive_template_param_or_substitution_call;
-       if (!(recursive_template_param_or_substitution_call = qualifiers))
-       {
-          qualifier_list<Allocator>* raw_qualifiers = M_qualifier_list_alloc.allocate(1);
-         qualifiers = new (raw_qualifiers) qualifier_list<Allocator>(*this);
-       }
-       // First eat all qualifiers.
-       bool failure = false;
-       for(;;)         // So we can use 'continue' to eat the next qualifier.
-       {
-         int start_pos = M_pos;
-         switch(current())
-         {
-           case 'P':
-             qualifiers->add_qualifier_start(pointer, start_pos,
-                 M_inside_substitution);
-             eat_current();
-             continue;
-           case 'R':
-             qualifiers->add_qualifier_start(reference, start_pos,
-                 M_inside_substitution);
-             eat_current();
-             continue;
-           case 'K':
-           case 'V':
-           case 'r':
-           {
-             char c;
-             int count = 0;
-             do
-             {
-               ++count;
-               c = next();
-             }
-             while(c == 'K' || c == 'V' || c == 'r');
-             qualifiers->add_qualifier_start(cv_qualifier, start_pos, count,
-                 M_inside_substitution);
-             continue;
-           }
-           case 'U':
-           {
-             eat_current();
-             string_type source_name;
-             if (!decode_source_name(source_name))
-             {
-               failure = true;
-               break;
-             }
-             qualifiers->add_qualifier_start(vendor_extension, start_pos,
-                 source_name, M_inside_substitution);
-             continue;
-           }
-           case 'A':
-           {
-             // <array-type> ::= A <positive dimension number> _ <element type>
-             //              ::= A [<dimension expression>] _ <element type>
-             //
-             string_type index;
-             int saved_pos;
-             store(saved_pos);
-             if (next() == 'n' || !decode_number(index))
-             {
-               restore(saved_pos);
-               if (next() != '_' && !decode_expression(index))
-               {
-                 failure = true;
-                 break;
-               }
-             }
-             if (eat_current() != '_')
-             {
-               failure = true;
-               break;
-             }
-             qualifiers->add_qualifier_start(array, start_pos, index,
-                 M_inside_substitution);
-             continue;
-           }
-           case 'M':
-           {
-             // <pointer-to-member-type> ::= M <class type> <member type>
-             // <Q>M<C> or <Q>M<C><Q2>F<R><B>E
-             eat_current();
-             string_type class_type;
-             if (!decode_type(class_type))             // Substitution: "<C>".
-             {
-               failure = true;
-               break;
-             }
-             char c = current();
-             if (c == 'F' || c == 'K' || c == 'V' || c == 'r')
-                 // Must be CV-qualifiers and a member function pointer.
-             {
-               // <Q>M<C><Q2>F<R><B>E  ==> R (C::*Q)B Q2
-               //     substitutions: "<C>", "F<R><B>E" (<R> and <B>
-               //                    recursive), "M<C><Q2>F<R><B>E".
-               int count = 0;
-               int Q2_start_pos = M_pos;
-               while(c == 'K' || c == 'V' || c == 'r')         // Decode <Q2>.
-               {
-                 ++count;
-                 c = next();
-               }
-               qualifier_list<Allocator> class_type_qualifiers(*this);
-               if (count)
-                 class_type_qualifiers.
-                     add_qualifier_start(cv_qualifier, Q2_start_pos,
-                         count, M_inside_substitution);
-               string_type member_function_qualifiers;
-               // It is unclear why g++ doesn't add a substitution for
-               // "<Q2>F<R><B>E" as it should I think.
-               string_type member_function_qualifiers_postfix;
-               class_type_qualifiers.
-                   decode_qualifiers(member_function_qualifiers,
-                       member_function_qualifiers_postfix, true);
-               member_function_qualifiers +=
-                   member_function_qualifiers_postfix;
-               // I don't think this substitution is actually ever used.
-               int function_pos = M_pos;
-               if (eat_current() != 'F')
-               {
-                 failure = true;
-                 break;
-               }
-               // Return type.
-               // Constructors, destructors and conversion operators don't
-               // have a return type, but seem to never get here.
-               string_type return_type_postfix;
-               if (!decode_type_with_postfix(prefix, return_type_postfix))
-                   // substitution: <R> recursive
-               {
-                 failure = true;
-                 break;
-               }
-               prefix += " (";
-               prefix += class_type;
-               prefix += "::*";
-               string_type bare_function_type;
-               if (!decode_bare_function_type(bare_function_type)
-                   || eat_current() != 'E')    // Substitution: <B> recursive.
-               {
-                 failure = true;
-                 break;
-               }
-               // substitution: "F<R><B>E".
-               add_substitution(function_pos, type);
-               // substitution: "M<C><Q2>F<R><B>E".
-               add_substitution(start_pos, type);
-               // substitution: all qualified types if any.
-               qualifiers->decode_qualifiers(prefix, postfix);
-               postfix += ")";
-               postfix += bare_function_type;
-               postfix += member_function_qualifiers;
-               postfix += return_type_postfix;
-               goto decode_type_exit;
-             }
-             qualifiers->add_qualifier_start(pointer_to_member, start_pos,
-                 class_type, M_inside_substitution);
-             continue;
-           }
-           default:
-             break;
-         }
-         break;
-       }
-       if (!failure)
-       {
-         // <Q>G<T>                    ==> imaginary T Q
-         //     substitutions: "<T>", "G<T>" (<T> recursive).
-         // <Q>C<T>                    ==> complex T Q
-         //     substitutions: "<T>", "C<T>" (<T> recursive).
-         if (current() == 'C' || current() == 'G')
-         {
-           prefix += current() == 'C' ? "complex " : "imaginary ";
-           qualifiers->add_qualifier_start(complex_or_imaginary, M_pos,
-               M_inside_substitution);
-           eat_current();
-         }
-         int start_pos = M_pos;
-         switch(current())
-         {
-           case 'F':
-           {
-             // <function-type> ::= F [Y] <bare-function-type> E
-             //
-             // Note that g++ never generates the 'Y', but we try to
-             // demangle it anyway.
-             bool extern_C = (next() == 'Y');
-             if (extern_C)
-               eat_current();
-
-             // <Q>F<R><B>E            ==> R (Q)B
-             //     substitution: "<R>", "<B>" (<B> recursive) and "F<R><B>E".
-
-             // Return type.
-             string_type return_type_postfix;
-             if (!decode_type_with_postfix(prefix, return_type_postfix))
-                 // Substitution: "<R>".
-             {
-               failure = true;
-               break;
-             }
-             // Only array and function (pointer) types have a postfix.
-             // In that case we don't want the space but expect something
-             // like prefix is "int (*" and postfix is ") [1]".
-             // We do want the space if this pointer is qualified.
-             if (return_type_postfix.size() == 0 ||
-                 (prefix.size() > 0 && *prefix.rbegin() != '*'))
-               prefix += ' ';
-             prefix += '(';
-             string_type bare_function_type;
-             if (!decode_bare_function_type(bare_function_type)
-                 // substitution: "<B>" (<B> recursive).
-                 || eat_current() != 'E')
-             {
-               failure = true;
-               break;
-             }
-             add_substitution(start_pos, type);  // Substitution: "F<R><B>E".
-             qualifiers->decode_qualifiers(prefix, postfix);
-                 // substitution: all qualified types, if any.
-             postfix += ")";
-             if (extern_C)
-               postfix += " [extern \"C\"] ";
-             postfix += bare_function_type;
-             postfix += return_type_postfix;
-             break;
-           }
-           case 'T':
-             if (!decode_template_param(prefix, qualifiers))
-             {
-               failure = true;
-               break;
-             }
-             if (current() == 'I')
-             {
-               add_substitution(start_pos, template_template_param);
-                   // substitution: "<template-template-param>".
-               if (!decode_template_args(prefix))
-               {
-                 failure = true;
-                 break;
-               }
-             }
-             if (!recursive_template_param_or_substitution_call
-                 && qualifiers->suppressed())
-             {
-               add_substitution(start_pos, type);
-                   // substitution: "<template-param>" or
-                   // "<template-template-param> <template-args>".
-               qualifiers->decode_qualifiers(prefix, postfix);
-                   // substitution: all qualified types, if any.
-             }
-             break;
-           case 'S':
-             if (M_pos >= M_maxpos)
-             {
-               failure = true;
-               break;
-             }
-             if (M_str[M_pos + 1] != 't')
-             {
-               if (!decode_substitution(prefix, qualifiers))
-               {
-                 failure = true;
-                 break;
-               }
-               if (current() == 'I')
-               {
-                 if (!decode_template_args(prefix))
-                 {
-                   failure = true;
-                   break;
-                 }
-                 if (!recursive_template_param_or_substitution_call
-                     && qualifiers->suppressed())
-                   add_substitution(start_pos, type);
-                       // Substitution:
-                       //   "<template-template-param> <template-args>".
-               }
-               if (!recursive_template_param_or_substitution_call
-                   && qualifiers->suppressed())
-                 qualifiers->decode_qualifiers(prefix, postfix);
-                     // Substitution: all qualified types, if any.
-               break;
-             }
-             /* Fall-through for St */
-           case 'N':
-           case 'Z':
-           case '0':
-           case '1':
-           case '2':
-           case '3':
-           case '4':
-           case '5':
-           case '6':
-           case '7':
-           case '8':
-           case '9':
-             // <Q><T>                 ==> T Q
-             //     substitutions: "<T>" (<T> recursive).
-             if (!decode_class_enum_type(prefix))
-             {
-               failure = true;
-               break;
-             }
-             if (!recursive_template_param_or_substitution_call)
-             {
-               add_substitution(start_pos, type);
-                   // substitution: "<class-enum-type>".
-               qualifiers->decode_qualifiers(prefix, postfix);
-                   // substitution: all qualified types, if any.
-             }
-             else
-               qualifiers->printing_suppressed();
-             break;
-           default:
-             // <Q><T>                 ==> T Q
-             //     substitutions: "<T>" (<T> recursive).
-             if (!decode_builtin_type(prefix))
-             {
-               failure = true;
-               break;
-             }
-             // If decode_type was called from decode_template_param then we
-             // need to suppress calling qualifiers here in order to get a
-             // substitution added anyway (for the <template-param>).
-             if (!recursive_template_param_or_substitution_call)
-               qualifiers->decode_qualifiers(prefix, postfix);
-             else
-               qualifiers->printing_suppressed();
-             break;
-         }
-       }
-    decode_type_exit:
-       --M_inside_type;
-       if (!recursive_template_param_or_substitution_call)
-       {
-         qualifiers->~qualifier_list<Allocator>();
-         M_qualifier_list_alloc.deallocate(qualifiers, 1);
-       }
-       if (failure)
-         _GLIBCXX_DEMANGLER_FAILURE;
-       _GLIBCXX_DEMANGLER_RETURN2;
-      }
-
-    // <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
-    //               ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
-    //
-    // <prefix> ::= <prefix> <unqualified-name>
-    //          ::= <template-prefix> <template-args>
-    //          ::= <template-param>
-    //          ::= # empty
-    //          ::= <substitution>
-    //
-    // <template-prefix> ::= <prefix> <template unqualified-name>
-    //                   ::= <template-param>
-    //                   ::= <substitution>
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_nested_name(string_type& output,
-                                            string_type& qualifiers)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_nested_name");
-
-       if (current() != 'N' || M_pos >= M_maxpos)
-         _GLIBCXX_DEMANGLER_FAILURE;
-
-       // <CV-qualifiers> ::= [r] [V] [K]  # restrict (C99), volatile, const
-       char const* qualifiers_start = &M_str[M_pos + 1];
-       for (char c = next(); c == 'K' || c == 'V' || c == 'r'; c = next());
-       for (char const* qualifier_ptr = &M_str[M_pos - 1];
-            qualifier_ptr >= qualifiers_start; --qualifier_ptr)
-         switch(*qualifier_ptr)
-         {
-           case 'K':
-             qualifiers += " const";
-             break;
-           case 'V':
-             qualifiers += " volatile";
-             break;
-           case 'r':
-             qualifiers += " restrict";
-             break;
-         }
-
-       int number_of_prefixes = 0;
-       int substitution_start = M_pos;
-       for(;;)
-       {
-         ++number_of_prefixes;
-         if (current() == 'S')
-         {
-           if (!decode_substitution(output))
-             _GLIBCXX_DEMANGLER_FAILURE;
-         }
-         else if (current() == 'I')
-         {
-           if (!decode_template_args(output))
-             _GLIBCXX_DEMANGLER_FAILURE;
-           if (current() != 'E')
-           {
-             // substitution: "<template-prefix> <template-args>".
-             add_substitution(substitution_start, nested_name_prefix,
-                              number_of_prefixes);
-           }
-         }
-         else
-         {
-           if (current() == 'T')
-           {
-             if (!decode_template_param(output))
-               _GLIBCXX_DEMANGLER_FAILURE;
-           }
-           else if (!decode_unqualified_name(output))
-             _GLIBCXX_DEMANGLER_FAILURE;
-           if (current() != 'E')
-           {
-             // substitution: "<prefix> <unqualified-name>" or
-             // "<prefix> <template unqualified-name>".
-             add_substitution(substitution_start,
-                 (current() == 'I') ?  nested_name_template_prefix
-                                    : nested_name_prefix,
-                 number_of_prefixes);
-           }
-         }
-         if (current() == 'E')
-         {
-           eat_current();
-           _GLIBCXX_DEMANGLER_RETURN;
-         }
-         if (current() != 'I')
-           output += "::";
-         else if (M_template_args_need_space)
-           output += ' ';
-         M_template_args_need_space = false;
-       }
-       _GLIBCXX_DEMANGLER_FAILURE;
-      }
-
-    // <local-name> := Z <function encoding> E <entity name> [<discriminator>]
-    //              := Z <function encoding> E s [<discriminator>]
-    // <discriminator> := _ <non-negative number>
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_local_name(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_local_name");
-       if (current() != 'Z' || M_pos >= M_maxpos)
-         _GLIBCXX_DEMANGLER_FAILURE;
-       if ((M_pos += decode_encoding(output, M_str + M_pos + 1,
-               M_maxpos - M_pos, M_implementation_details) + 1) < 0 ||
-               eat_current() != 'E')
-         _GLIBCXX_DEMANGLER_FAILURE;
-       output += "::";
-       if (current() == 's')
-       {
-         eat_current();
-         output += "string literal";
-       }
-       else
-       {
-         string_type nested_name_qualifiers;
-         if (!decode_name(output, nested_name_qualifiers))
-           _GLIBCXX_DEMANGLER_FAILURE;
-         output += nested_name_qualifiers;
-       }
-       string_type discriminator;
-       if (current() == '_' && next() != 'n' && !decode_number(discriminator))
-         _GLIBCXX_DEMANGLER_FAILURE;
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <source-name> ::= <positive length number> <identifier>
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_source_name(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_source_name");
-       int length = current() - '0';
-       if (length < 1 || length > 9)
-         _GLIBCXX_DEMANGLER_FAILURE;
-       while(isdigit(next()))
-         length = 10 * length + current() - '0';
-       char const* ptr = &M_str[M_pos];
-       if (length > 11 && !strncmp(ptr, "_GLOBAL_", 8) && ptr[9] == 'N'
-           && ptr[8] == ptr[10])
-       {
-         output += "(anonymous namespace)";
-         if ((M_pos += length) > M_maxpos + 1)
-           _GLIBCXX_DEMANGLER_FAILURE;
-       }
-       else
-         while(length--)
-         {
-           if (current() == 0)
-             _GLIBCXX_DEMANGLER_FAILURE;
-           output += eat_current();
-         }
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <unqualified-name> ::= <operator-name>  # Starts with lower case.
-    //                    ::= <ctor-dtor-name>  # Starts with 'C' or 'D'.
-    //                    ::= <source-name>    # Starts with a digit.
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_unqualified_name(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_unqualified_name");
-       if (M_inside_template_args)
-       {
-         if (!decode_source_name(output))
-           _GLIBCXX_DEMANGLER_FAILURE;
-       }
-       else if (isdigit(current()))
-       {
-         bool recursive_unqualified_name = (&M_function_name == &output);
-         // This can be a recursive call when we are decoding
-         // an <operator-name> that is a cast operator for a some
-         // <unqualified-name>; for example "operator Foo()".
-         // In that case this is thus not a ctor or dtor and we
-         // are not interested in updating M_function_name.
-         if (!recursive_unqualified_name)
-           M_function_name.clear();
-         M_name_is_template = false;
-         M_name_is_cdtor = false;
-         M_name_is_conversion_operator = false;
-         if (!decode_source_name(M_function_name))
-           _GLIBCXX_DEMANGLER_FAILURE;
-         if (!recursive_unqualified_name)
-           output += M_function_name;
-       }
-       else if (islower(current()))
-       {
-         M_function_name.clear();
-         M_name_is_template = false;
-         M_name_is_cdtor = false;
-         M_name_is_conversion_operator = false;
-         if (!decode_operator_name(M_function_name))
-           _GLIBCXX_DEMANGLER_FAILURE;
-         output += M_function_name;
-       }
-       else if (current() == 'C' || current() == 'D')
-       {
-         // <ctor-dtor-name> ::=
-         //   C1       # complete object (in-charge) constructor
-         //   C2       # base object (not-in-charge) constructor
-         //   C3       # complete object (in-charge) allocating constructor
-         //   D0       # deleting (in-charge) destructor
-         //   D1       # complete object (in-charge) destructor
-         //   D2       # base object (not-in-charge) destructor
-         //
-         if (current() == 'C')
-         {
-           char c = next();
-           if (c < '1' || c > '3')
-             _GLIBCXX_DEMANGLER_FAILURE;
-         }
-         else
-         {
-           char c = next();
-           if (c < '0' || c > '2')
-             _GLIBCXX_DEMANGLER_FAILURE;
-           output += '~';
-           M_saw_destructor = true;
-         }
-         M_name_is_cdtor = true;
-         eat_current();
-         output += M_function_name;
-       }
-       else
-         _GLIBCXX_DEMANGLER_FAILURE;
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <unscoped-name> ::=
-    //   <unqualified-name>            # Starts not with an 'S'
-    //   St <unqualified-name>         # ::std::
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_unscoped_name(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_unscoped_name");
-       if (current() == 'S')
-       {
-         if (next() != 't')
-           _GLIBCXX_DEMANGLER_FAILURE;
-         eat_current();
-         output += "std::";
-       }
-       decode_unqualified_name(output);
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <name> ::=
-    //   <nested-name>                         # Starts with 'N'
-    //   <unscoped-template-name> <template-args> # idem
-    //   <local-name>                          # Starts with 'Z'
-    //   <unscoped-name>                       # Starts with 'S', 'C', 'D',
-    //                                         # a digit or a lower case
-    //                                         # character.
-    //
-    // <unscoped-template-name> ::= <unscoped-name>
-    //                          ::= <substitution>
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_name(string_type& output,
-                                     string_type& nested_name_qualifiers)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_name");
-       int substitution_start = M_pos;
-       if (current() == 'S' && (M_pos >= M_maxpos || M_str[M_pos + 1] != 't'))
-       {
-         if (!decode_substitution(output))
-           _GLIBCXX_DEMANGLER_FAILURE;
-       }
-       else if (current() == 'N')
-       {
-         decode_nested_name(output, nested_name_qualifiers);
-         _GLIBCXX_DEMANGLER_RETURN;
-       }
-       else if (current() == 'Z')
-       {
-         decode_local_name(output);
-         _GLIBCXX_DEMANGLER_RETURN;
-       }
-       else if (!decode_unscoped_name(output))
-         _GLIBCXX_DEMANGLER_FAILURE;
-       if (current() == 'I')
-       {
-         // Must have been an <unscoped-template-name>.
-         add_substitution(substitution_start, unscoped_template_name);
-         if (!decode_template_args(output))
-           _GLIBCXX_DEMANGLER_FAILURE;
-       }
-       M_template_args_need_space = false;
-       _GLIBCXX_DEMANGLER_RETURN;
-      }
-
-    // <call-offset> ::= h <nv-offset> _
-    //               ::= v <v-offset> _
-    // <nv-offset>   ::= <offset number>
-    //     non-virtual base override
-    //
-    // <v-offset>    ::= <offset number> _ <virtual offset number>
-    //     virtual base override, with vcall offset
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_call_offset(string_type&
-#if _GLIBCXX_DEMANGLER_CWDEBUG
-         output
-#endif
-         )
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_call_offset");
-       if (current() == 'h')
-       {
-         string_type dummy;
-         eat_current();
-         if (decode_number(dummy) && current() == '_')
-         {
-           eat_current();
-           _GLIBCXX_DEMANGLER_RETURN;
-         }
-       }
-       else if (current() == 'v')
-       {
-         string_type dummy;
-         eat_current();
-         if (decode_number(dummy) && current() == '_')
-         {
-           eat_current();
-           if (decode_number(dummy) && current() == '_')
-           {
-             eat_current();
-             _GLIBCXX_DEMANGLER_RETURN;
-           }
-         }
-       }
-       _GLIBCXX_DEMANGLER_FAILURE;
-      }
-
-    //
-    // <special-name> ::=
-    //   TV <type>                     # virtual table
-    //   TT <type>                     # VTT structure (construction
-    //                                    vtable index).
-    //   TI <type>                     # typeinfo structure
-    //   TS <type>                     # typeinfo name (null-terminated
-    //                                    byte string).
-    //   GV <object name>              # Guard variable for one-time
-    //                                   initialization of static objects in
-    //                                   a local scope.
-    //   T <call-offset> <base encoding># base is the nominal target function
-    //                                   of thunk.
-    //   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
-    //
-    template<typename Allocator>
-      bool
-      session<Allocator>::decode_special_name(string_type& output)
-      {
-       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_special_name");
-       if (current() == 'G')
-       {
-         if (next() != 'V')
-           _GLIBCXX_DEMANGLER_FAILURE;
-         output += "guard variable for ";
-         string_type nested_name_qualifiers;
-         eat_current();
-         if (!decode_name(output, nested_name_qualifiers))
-           _GLIBCXX_DEMANGLER_FAILURE;
-         output += nested_name_qualifiers;
-         _GLIBCXX_DEMANGLER_RETURN;
-       }
-       else if (current() != 'T')
-         _GLIBCXX_DEMANGLER_FAILURE;
-       switch(next())
-       {
-         case 'V':
-           output += "vtable for ";
-           eat_current();
-           decode_type(output);
-           _GLIBCXX_DEMANGLER_RETURN;
-         case 'T':
-           output += "VTT for ";
-           eat_current();
-           decode_type(output);
-           _GLIBCXX_DEMANGLER_RETURN;
-         case 'I':
-           output += "typeinfo for ";
-           eat_current();
-           decode_type(output);
-           _GLIBCXX_DEMANGLER_RETURN;
-         case 'S':
-           output += "typeinfo name for ";
-           eat_current();
-           decode_type(output);
-           _GLIBCXX_DEMANGLER_RETURN;
-         case 'c':
-           output += "covariant return thunk to ";
-           if (!decode_call_offset(output)
-               || !decode_call_offset(output)
-               || (M_pos += decode_encoding(output, M_str + M_pos,
-                   M_maxpos - M_pos + 1, M_implementation_details)) < 0)
-             _GLIBCXX_DEMANGLER_FAILURE;
-           _GLIBCXX_DEMANGLER_RETURN;
-         case 'C':             // GNU extension?
-         {
-           string_type first;
-           output += "construction vtable for ";
-           eat_current();
-           if (!decode_type(first))
-             _GLIBCXX_DEMANGLER_FAILURE;
-           while(isdigit(current()))
-             eat_current();
-           if (eat_current() != '_')
-             _GLIBCXX_DEMANGLER_FAILURE;
-           if (!decode_type(output))
-             _GLIBCXX_DEMANGLER_FAILURE;
-           output += "-in-";
-           output += first;
-           _GLIBCXX_DEMANGLER_RETURN;
-         }
-         default:
-           if (current() == 'v')
-             output += "virtual thunk to ";
-           else
-             output += "non-virtual thunk to ";
-           if (!decode_call_offset(output)
-               || (M_pos += decode_encoding(output, M_str + M_pos,
-                   M_maxpos - M_pos + 1, M_implementation_details)) < 0)
-             _GLIBCXX_DEMANGLER_FAILURE;
-           _GLIBCXX_DEMANGLER_RETURN;
-       }
-      }
-
-    // <encoding> ::=
-    //   <function name> <bare-function-type>  # Starts with 'C', 'D', 'N',
-    //                                           'S', a digit or a lower case
-    //                                           character.
-    //   <data name>                           # Idem.
-    //   <special-name>                                # Starts with 'T' or 'G'.
-    template<typename Allocator>
-      int
-      session<Allocator>::decode_encoding(string_type& output,
-          char const* in, int len, implementation_details const& id)
-      {
-#if _GLIBCXX_DEMANGLER_CWDEBUG
-       _GLIBCXX_DEMANGLER_DOUT(dc::demangler,
-           "Output thus far: \"" << output << '"');
-       string_type input(in, len > 0x40000000 ? strlen(in) : len);
-       _GLIBCXX_DEMANGLER_DOUT(
-           dc::demangler, "Entering decode_encoding(\"" << input << "\")");
-#endif
-       if (len <= 0)
-         return INT_MIN;
-       session<Allocator> demangler_session(in, len, id);
-       string_type nested_name_qualifiers;
-       int saved_pos;
-       demangler_session.store(saved_pos);
-       if (demangler_session.decode_special_name(output))
-         return demangler_session.M_pos;
-       demangler_session.restore(saved_pos);
-       string_type name;
-       if (!demangler_session.decode_name(name, nested_name_qualifiers))
-         return INT_MIN;
-       if (demangler_session.current() == 0
-           || demangler_session.current() == 'E')
-       {
-         output += name;
-         output += nested_name_qualifiers;
-         return demangler_session.M_pos;
-       }
-       // Must have been a <function name>.
-       string_type return_type_postfix;
-       if (demangler_session.M_name_is_template
-           && !(demangler_session.M_name_is_cdtor
-                || demangler_session.M_name_is_conversion_operator))
-       {
-         // Return type of function
-         if (!demangler_session.decode_type_with_postfix(output,
-             return_type_postfix))
-           return INT_MIN;
-         output += ' ';
-       }
-       output += name;
-       if (!demangler_session.decode_bare_function_type(output))
-         return INT_MIN;
-       output += nested_name_qualifiers;
-       output += return_type_postfix;
-       return demangler_session.M_pos;
-      }
-
-    } // namespace demangler
-
-  // Public interface
-  template<typename Allocator>
-    struct demangle
-    {
-      typedef typename Allocator::template rebind<char>::other char_Allocator;
-      typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
-         string_type;
-      static string_type symbol(char const* in,
-                                demangler::implementation_details const& id);
-      static string_type type(char const* in,
-                              demangler::implementation_details const& id);
-    };
-
-  // demangle::symbol()
-  //
-  // Demangle `input' which should be a mangled function name as for
-  // instance returned by nm(1).
-  template<typename Allocator>
-    typename demangle<Allocator>::string_type
-    demangle<Allocator>::symbol(char const* input,
-                                demangler::implementation_details const& id)
-    {
-      // <mangled-name> ::= _Z <encoding>
-      // <mangled-name> ::= _GLOBAL_ _<type>_ <disambiguation part>
-      //                    <type> can be I or D (GNU extension)
-      typedef demangler::session<Allocator> demangler_type;
-      string_type result;
-      bool failure = (input[0] != '_');
-
-      if (!failure)
-      {
-       if (input[1] == 'G')
-       {
-         if (!strncmp(input, "_GLOBAL__", 9)
-             && (input[9] == 'D' || input[9] == 'I')
-             && input[10] == '_')
-         {
-           if (input[9] == 'D')
-             result.assign("global destructors keyed to ", 28);
-           else
-             result.assign("global constructors keyed to ", 29);
-           // Output the disambiguation part as-is.
-           result += input + 11;
-         }
-         else
-           failure = true;
-       }
-       else if (input[1] == 'Z')
-       {
-         int cnt =
-             demangler_type::decode_encoding(result, input + 2, INT_MAX, id);
-         if (cnt < 0 || input[cnt + 2] != 0)
-           failure = true;
-       }
-       else
-         failure = true;
-      }
-
-      // Failure to demangle, return the mangled name.
-      if (failure)
-       result.assign(input, strlen(input));
-
-      return result;
-    }
-
-  // demangle::type()
-  // Demangle `input' which must be a zero terminated mangled type
-  // name as for instance returned by std::type_info::name().
-  template<typename Allocator>
-    typename demangle<Allocator>::string_type
-    demangle<Allocator>::type(char const* input,
-                              demangler::implementation_details const& id)
-    {
-      std::basic_string<char, std::char_traits<char>, Allocator> result;
-      if (input == NULL)
-       result = "(null)";
-      else
-      {
-       demangler::session<Allocator> demangler_session(input, INT_MAX, id);
-       if (!demangler_session.decode_type(result)
-           || demangler_session.remaining_input_characters())
-       {
-         // Failure to demangle, return the mangled name.
-         result = input;
-       }
-      }
-      return result;
-    }
-
-} // namespace __gnu_cxx
-
-#endif // __DEMANGLE_H
diff --git a/libstdc++-v3/include/ext/demangle.h b/libstdc++-v3/include/ext/demangle.h
new file mode 100644 (file)
index 0000000..5de4f04
--- /dev/null
@@ -0,0 +1,2789 @@
+// C++ IA64 / g++ v3 demangler  -*- C++ -*-
+
+// Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+// Written by Carlo Wood <carlo@alinoe.com>
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING.  If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction.  Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License.  This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+// This file implements demangling of "C++ ABI for Itanium"-mangled symbol
+// and type names as described in Revision 1.73 of the C++ ABI as can be found
+// at http://www.codesourcery.com/cxx-abi/abi.html#mangling
+
+#ifndef _DEMANGLER_H
+#define _DEMANGLER_H 1
+
+#include <vector>
+#include <string>
+#include <ext/new_allocator.h>
+
+#ifndef _GLIBCXX_DEMANGLER_DEBUG
+#define _GLIBCXX_DEMANGLER_CWDEBUG 0
+#define _GLIBCXX_DEMANGLER_DEBUG(x)
+#define _GLIBCXX_DEMANGLER_DOUT(cntrl, data)
+#define _GLIBCXX_DEMANGLER_DOUT_ENTERING(x)
+#define _GLIBCXX_DEMANGLER_DOUT_ENTERING2(x)
+#define _GLIBCXX_DEMANGLER_DOUT_ENTERING3(x)
+#define _GLIBCXX_DEMANGLER_RETURN return M_result
+#define _GLIBCXX_DEMANGLER_RETURN2 return M_result
+#define _GLIBCXX_DEMANGLER_RETURN3
+#define _GLIBCXX_DEMANGLER_FAILURE \
+    do { M_result = false; return false; } while(0)
+#else
+#define _GLIBCXX_DEMANGLER_CWDEBUG 1
+#endif
+
+namespace __gnu_cxx
+{
+  namespace demangler
+  {
+    enum substitution_nt
+    {
+      type,
+      template_template_param,
+      nested_name_prefix,
+      nested_name_template_prefix,
+      unscoped_template_name
+    };
+
+    struct substitution_st
+    {
+      int M_start_pos;
+      substitution_nt M_type;
+      int M_number_of_prefixes;
+
+      substitution_st(int start_pos,
+                     substitution_nt type,
+                     int number_of_prefixes)
+      : M_start_pos(start_pos), M_type(type),
+       M_number_of_prefixes(number_of_prefixes)
+      { }
+    };
+
+    enum simple_qualifier_nt
+    {
+      complex_or_imaginary = 'G',
+      pointer = 'P',
+      reference = 'R'
+    };
+
+    enum cv_qualifier_nt
+    {
+      cv_qualifier = 'K'
+    };
+
+    enum param_qualifier_nt
+    {
+      vendor_extension = 'U',
+      array = 'A',
+      pointer_to_member = 'M'
+    };
+
+    template<typename Tp, typename Allocator = __gnu_cxx::new_allocator<Tp> >
+      class qualifier;
+
+    template<typename Tp, typename Allocator = __gnu_cxx::new_allocator<Tp> >
+      class qualifier_list;
+
+    template<typename Tp, typename Allocator = __gnu_cxx::new_allocator<Tp> >
+      class session;
+
+    template<typename Tp, typename Allocator>
+      class qualifier
+      {
+       typedef typename Allocator::template rebind<char>::other
+               char_Allocator;
+       typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
+           string_type;
+
+      private:
+       char M_qualifier1;
+       char M_qualifier2;
+       char M_qualifier3;
+       mutable unsigned char M_cnt;
+       string_type M_optional_type;
+       int M_start_pos;
+       bool M_part_of_substitution;
+
+      public:
+       qualifier(int start_pos,
+                 simple_qualifier_nt simple_qualifier,
+                 int inside_substitution)
+       : M_qualifier1(simple_qualifier),
+         M_start_pos(start_pos),
+         M_part_of_substitution(inside_substitution)
+       { }
+
+       qualifier(int start_pos,
+                 cv_qualifier_nt,
+                 char const* start,
+                 int count,
+                 int inside_substitution)
+       : M_qualifier1(start[0]),
+         M_qualifier2((count > 1) ? start[1] : '\0'),
+         M_qualifier3((count > 2) ? start[2] : '\0'),
+         M_start_pos(start_pos),
+         M_part_of_substitution(inside_substitution)
+       { }
+
+       qualifier(int start_pos,
+                 param_qualifier_nt param_qualifier,
+                 string_type optional_type,
+                 int inside_substitution)
+       : M_qualifier1(param_qualifier),
+         M_optional_type(optional_type),
+         M_start_pos(start_pos),
+         M_part_of_substitution(inside_substitution)
+       { }
+
+       int
+       get_start_pos(void) const
+       { return M_start_pos; }
+
+       char
+       first_qualifier(void) const
+       { M_cnt = 1; return M_qualifier1; }
+
+       char
+       next_qualifier(void) const
+       {
+         return (++M_cnt == 2) ? M_qualifier2
+                               : ((M_cnt == 3) ? M_qualifier3 : 0);
+       }
+
+       string_type const&
+       get_optional_type(void) const
+       { return M_optional_type; }
+
+       bool
+       part_of_substitution(void) const
+       { return M_part_of_substitution; }
+
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+       friend std::ostream& operator<<(std::ostream& os, qualifier const& qual)
+       {
+         os << (char)qual.M_qualifier1;
+         if (qual.M_qualifier1 == vendor_extension ||
+             qual.M_qualifier1 == array ||
+             qual.M_qualifier1 == pointer_to_member)
+           os << " [" << qual.M_optional_type << ']';
+         else if (qual.M_qualifier1 == 'K' ||
+                  qual.M_qualifier1 == 'V' ||
+                  qual.M_qualifier1 == 'r')
+         {
+           if (qual.M_qualifier2)
+           {
+             os << (char)qual.M_qualifier2;
+             if (qual.M_qualifier3)
+               os << (char)qual.M_qualifier3;
+           }
+         }
+         return os;
+       }
+#endif
+      };
+
+    template<typename Tp, typename Allocator>
+      class qualifier_list
+      {
+       typedef typename Allocator::template rebind<char>::other
+         char_Allocator;
+       typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
+         string_type;
+
+      private:
+       mutable bool M_printing_suppressed;
+       typedef qualifier<Tp, Allocator> qual;
+        typedef typename Allocator::template rebind<qual>::other qual_Allocator;
+       typedef std::vector<qual, qual_Allocator> qual_vector;
+       qual_vector M_qualifier_starts;
+       session<Tp, Allocator>& M_demangler;
+
+       void decode_KVrA(string_type& prefix, string_type& postfix, int cvq,
+                        typename qual_vector::
+                          const_reverse_iterator const& iter_array) const;
+
+      public:
+       qualifier_list(session<Tp, Allocator>& demangler_obj)
+       : M_printing_suppressed(false), M_demangler(demangler_obj)
+       { }
+
+       void
+       add_qualifier_start(simple_qualifier_nt simple_qualifier,
+                           int start_pos,
+                           int inside_substitution)
+       { M_qualifier_starts.
+             push_back(qualifier<Tp, Allocator>(start_pos,
+                 simple_qualifier, inside_substitution)); }
+
+       void
+       add_qualifier_start(cv_qualifier_nt cv_qualifier,
+                           int start_pos,
+                           int count,
+                           int inside_substitution)
+       { M_qualifier_starts.
+             push_back(qualifier<Tp, Allocator>(start_pos,
+                   cv_qualifier, &M_demangler.M_str[start_pos],
+                   count, inside_substitution)); }
+
+       void
+       add_qualifier_start(param_qualifier_nt param_qualifier,
+                           int start_pos,
+                           string_type optional_type,
+                           int inside_substitution)
+       { M_qualifier_starts.
+             push_back(qualifier<Tp, Allocator>(start_pos,
+                   param_qualifier, optional_type, inside_substitution)); }
+
+       void
+       decode_qualifiers(string_type& prefix,
+                         string_type& postfix,
+                         bool member_function_pointer_qualifiers) const;
+
+       bool
+       suppressed(void) const
+       { return M_printing_suppressed; }
+
+       void
+       printing_suppressed(void)
+       { M_printing_suppressed = true; }
+
+       size_t
+       size(void) const
+       { return M_qualifier_starts.size(); }
+
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+       friend std::ostream& operator<<(std::ostream& os, qualifier_list const& list)
+       {
+         typename qual_vector::const_iterator
+             iter = list.M_qualifier_starts.begin();
+         if (iter != list.M_qualifier_starts.end())
+         {
+           os << "{ " << *iter;
+           while (++iter != list.M_qualifier_starts.end())
+             os << ", " << *iter;
+           os << " }";
+         }
+         else
+           os << "{ }";
+         return os;
+       }
+#endif
+      };
+
+    struct implementation_details
+    {
+      private:
+        unsigned int M_style;
+
+      public:
+       // The following flags change the behaviour of the demangler.  The
+       // default behaviour is that none of these flags is set.
+
+        static unsigned int const style_void = 1;
+       // Default behaviour:                           int f()
+       // Use (void) instead of ():                    int f(void)
+
+        static unsigned int const style_literal = 2;
+       // Default behaviour:                           (long)13,
+       //                                              (unsigned long long)19
+       // Use extensions 'u', 'l' and 'll' for integral
+       // literals (as in template arguments):         13l, 19ull
+
+        static unsigned int const style_literal_int = 4;
+       // Default behaviour:                           4
+       // Use also an explicit
+       //   cast for int in literals:                  (int)4
+
+        static unsigned int const style_compact_expr_ops = 8;
+       // Default behaviour:                           (i) < (3), sizeof (int)
+       // Don't output spaces around
+       //   operators in expressions:                  (i)<(3), sizeof(int)
+
+        static unsigned int const style_sizeof_typename = 16;
+       // Default behaviour:                           sizeof (X::t)
+       // Put 'typename' infront of <nested-name>
+       // types inside a 'sizeof':                     sizeof (typename X::t)
+
+      public:
+       implementation_details(unsigned int style_flags = 0) :
+           M_style(style_flags) { }
+       virtual ~implementation_details() { }
+       bool get_style_void(void) const
+           { return (M_style & style_void); }
+       bool get_style_literal(void) const
+           { return (M_style & style_literal); }
+       bool get_style_literal_int(void) const
+           { return (M_style & style_literal_int); }
+       bool get_style_compact_expr_ops(void) const
+           { return (M_style & style_compact_expr_ops); }
+       bool get_style_sizeof_typename(void) const
+           { return (M_style & style_sizeof_typename); }
+        // This can be overridden by user implementations.
+        virtual bool decode_real(char* /* output */, unsigned long* /* input */,
+                                size_t /* size_of_real */) const
+            { return false; }
+    };
+
+    template<typename Tp, typename Allocator>
+      class session
+      {
+      public:
+       friend class qualifier_list<Tp, Allocator>;
+       typedef typename Allocator::template rebind<char>::other
+           char_Allocator;
+       typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
+           string_type;
+
+      private:
+       char const* M_str;
+       int M_pos;
+       int M_maxpos;
+       bool M_result;
+       int M_inside_template_args;
+       int M_inside_type;
+       int M_inside_substitution;
+       bool M_saw_destructor;
+       bool M_name_is_cdtor;
+       bool M_name_is_template;
+       bool M_name_is_conversion_operator;
+       bool M_template_args_need_space;
+       string_type M_function_name;
+        typedef typename Allocator::template rebind<int>::other
+                int_Allocator;
+        typedef typename Allocator::template rebind<substitution_st>::other
+                subst_Allocator;
+       std::vector<int, int_Allocator> M_template_arg_pos;
+       int M_template_arg_pos_offset;
+       std::vector<substitution_st, subst_Allocator> M_substitutions_pos;
+       implementation_details const& M_implementation_details;
+       typedef typename Allocator::template
+           rebind<qualifier_list<Allocator> >::other qualifier_list_Allocator;
+       qualifier_list_Allocator M_qualifier_list_alloc;
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+       bool M_inside_add_substitution;
+#endif
+
+      public:
+       explicit session(char const* in, int len,
+           implementation_details const& id = implementation_details())
+       : M_str(in), M_pos(0), M_maxpos(len - 1), M_result(true),
+         M_inside_template_args(0), M_inside_type(0),
+         M_inside_substitution(0), M_saw_destructor(false),
+         M_name_is_cdtor(false), M_name_is_template(false),
+         M_name_is_conversion_operator(false),
+         M_template_args_need_space(false), M_template_arg_pos_offset(0),
+         M_implementation_details(id)
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+         , M_inside_add_substitution(false)
+#endif
+       { }
+
+       static int
+       decode_encoding(string_type& output, char const* input, int len,
+         implementation_details const& id = implementation_details());
+
+       bool
+       decode_type(string_type& output,
+                   qualifier_list<Tp, Allocator>* qualifiers = NULL)
+       {
+         string_type postfix;
+         bool res = decode_type_with_postfix(output, postfix, qualifiers);
+         output += postfix;
+         return res;
+       }
+
+       bool
+       remaining_input_characters(void) const
+       { return current() != 0; }
+
+      private:
+       char
+       current(void) const
+       { return (M_pos > M_maxpos) ? 0 : M_str[M_pos]; }
+
+       char
+       next_peek(void) const
+       { return (M_pos >= M_maxpos) ? 0 : M_str[M_pos + 1]; }
+
+       char
+       next(void)
+       { return (M_pos >= M_maxpos) ? 0 : M_str[++M_pos]; }
+
+       char
+       eat_current(void)
+       { return (M_pos > M_maxpos) ? 0 : M_str[M_pos++]; }
+
+       void
+       store(int& saved_pos)
+       { saved_pos = M_pos; }
+
+       void
+       restore(int saved_pos)
+       { M_pos = saved_pos; M_result = true; }
+
+       void
+       add_substitution(int start_pos,
+                        substitution_nt sub_type,
+                        int number_of_prefixes);
+
+       bool decode_type_with_postfix(string_type& prefix,
+           string_type& postfix, qualifier_list<Tp, Allocator>* qualifiers = NULL);
+       bool decode_bare_function_type(string_type& output);
+       bool decode_builtin_type(string_type& output);
+       bool decode_call_offset(string_type& output);
+       bool decode_class_enum_type(string_type& output);
+       bool decode_expression(string_type& output);
+       bool decode_literal(string_type& output);
+       bool decode_local_name(string_type& output);
+       bool decode_name(string_type& output,
+           string_type& nested_name_qualifiers);
+       bool decode_nested_name(string_type& output,
+           string_type& qualifiers);
+       bool decode_number(string_type& output);
+       bool decode_operator_name(string_type& output);
+       bool decode_source_name(string_type& output);
+       bool decode_substitution(string_type& output,
+           qualifier_list<Tp, Allocator>* qualifiers = NULL);
+       bool decode_template_args(string_type& output);
+       bool decode_template_param(string_type& output,
+           qualifier_list<Tp, Allocator>* qualifiers = NULL);
+       bool decode_unqualified_name(string_type& output);
+       bool decode_unscoped_name(string_type& output);
+       bool decode_non_negative_decimal_integer(string_type& output);
+       bool decode_special_name(string_type& output);
+        bool decode_real(string_type& output, size_t size_of_real);
+      };
+
+    template<typename Tp, typename Allocator>
+#if !_GLIBCXX_DEMANGLER_CWDEBUG
+      inline
+#endif
+      void
+      session<Tp, Allocator>::add_substitution(int start_pos,
+                                          substitution_nt sub_type,
+                                          int number_of_prefixes = 0)
+      {
+       if (!M_inside_substitution)
+       {
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+         if (M_inside_add_substitution)
+           return;
+#endif
+         M_substitutions_pos.
+             push_back(substitution_st(start_pos,
+                 sub_type, number_of_prefixes));
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+         if (!DEBUGCHANNELS::dc::demangler.is_on())
+           return;
+         string_type substitution_name("S");
+         int n = M_substitutions_pos.size() - 1;
+         if (n > 0)
+           substitution_name += (n <= 10) ? (char)(n + '0' - 1)
+                                          : (char)(n + 'A' - 11);
+         substitution_name += '_';
+         string_type subst;
+         int saved_pos = M_pos;
+         M_pos = start_pos;
+         M_inside_add_substitution = true;
+         _GLIBCXX_DEMANGLER_DEBUG( dc::demangler.off() );
+         switch(sub_type)
+         {
+           case type:
+             decode_type(subst);
+             break;
+           case template_template_param:
+             decode_template_param(subst);
+             break;
+           case nested_name_prefix:
+           case nested_name_template_prefix:
+             for (int cnt = number_of_prefixes; cnt > 0; --cnt)
+             {
+               if (current() == 'I')
+               {
+                 subst += ' ';
+                 decode_template_args(subst);
+               }
+               else
+               {
+                 if (cnt < number_of_prefixes)
+                   subst += "::";
+                 if (current() == 'S')
+                   decode_substitution(subst);
+                 else if (current() == 'T')
+                   decode_template_param(subst);
+                 else
+                   decode_unqualified_name(subst);
+               }
+             }
+             break;
+           case unscoped_template_name:
+             decode_unscoped_name(subst);
+             break;
+         }
+         M_pos = saved_pos;
+         _GLIBCXX_DEMANGLER_DEBUG( dc::demangler.on() );
+         _GLIBCXX_DEMANGLER_DOUT(dc::demangler,
+             "Adding substitution " << substitution_name
+             << " : " << subst
+             << " (from " << location_ct((char*)__builtin_return_address(0)
+                                         + builtin_return_address_offset)
+             << " <- " << location_ct((char*)__builtin_return_address(1)
+                                      + builtin_return_address_offset)
+             << " <- " << location_ct((char*)__builtin_return_address(2)
+                                      + builtin_return_address_offset)
+             << ").");
+         M_inside_add_substitution = false;
+#endif
+       }
+      }
+
+    // We don't want to depend on locale (or include <cctype> for that matter).
+    // We also don't want to use "safe-ctype.h" because that headerfile is not
+    // available to the users.
+    inline bool isdigit(char c) { return c >= '0' && c <= '9'; }
+    inline bool islower(char c) { return c >= 'a' && c <= 'z'; }
+    inline bool isupper(char c) { return c >= 'A' && c <= 'Z'; }
+    inline char tolower(char c) { return isupper(c) ? c - 'A' + 'a' : c; }
+
+    //
+    // <non-negative decimal integer> ::= 0
+    //                                ::= 1|2|3|4|5|6|7|8|9 [<digit>+]
+    // <digit>                        ::= 0|1|2|3|4|5|6|7|8|9
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::
+         decode_non_negative_decimal_integer(string_type& output)
+      {
+       char c = current();
+       if (c == '0')
+       {
+         output += '0';
+         eat_current();
+       }
+       else if (!isdigit(c))
+         M_result = false;
+       else
+       {
+         do
+         {
+           output += c;
+         }
+         while (isdigit((c = next())));
+       }
+       return M_result;
+      }
+
+    // <number> ::= [n] <non-negative decimal integer>
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_number(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_number");
+       if (current() != 'n')
+         decode_non_negative_decimal_integer(output);
+       else
+       {
+         output += '-';
+         eat_current();
+         decode_non_negative_decimal_integer(output);
+       }
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <builtin-type> ::= v  # void
+    //                ::= w  # wchar_t
+    //                ::= b  # bool
+    //                ::= c  # char
+    //                ::= a  # signed char
+    //                ::= h  # unsigned char
+    //                ::= s  # short
+    //                ::= t  # unsigned short
+    //                ::= i  # int
+    //                ::= j  # unsigned int
+    //                ::= l  # long
+    //                ::= m  # unsigned long
+    //                ::= x  # long long, __int64
+    //                ::= y  # unsigned long long, __int64
+    //                ::= n  # __int128
+    //                ::= o  # unsigned __int128
+    //                ::= f  # float
+    //                ::= d  # double
+    //                ::= e  # long double, __float80
+    //                ::= g  # __float128
+    //                ::= z  # ellipsis
+    //                ::= u <source-name>    # vendor extended type
+    //
+    char const* const builtin_type_c[26] =
+    {
+      "signed char",   // a
+      "bool",          // b
+      "char",          // c
+      "double",                // d
+      "long double",   // e
+      "float",         // f
+      "__float128",            // g
+      "unsigned char", // h
+      "int",           // i
+      "unsigned int",  // 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
+      "wchar_t",               // w
+      "long long",             // x
+      "unsigned long long",    // y
+      "..."                    // z
+    };
+
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_builtin_type(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_builtin_type");
+       char const* bt;
+       if (!islower(current()) || !(bt = builtin_type_c[current() - 'a']))
+         _GLIBCXX_DEMANGLER_FAILURE;
+       output += bt;
+       eat_current();
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <class-enum-type> ::= <name>
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_class_enum_type(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_class_enum_type");
+       string_type nested_name_qualifiers;
+       if (!decode_name(output, nested_name_qualifiers))
+         _GLIBCXX_DEMANGLER_FAILURE;
+       output += nested_name_qualifiers;
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <substitution> ::=
+    //   S <seq-id> _
+    //   S_
+    //   St # ::std::
+    //   Sa # ::std::allocator
+    //   Sb # ::std::basic_string
+    //   Ss # ::std::basic_string<char, std::char_traits<char>,
+    //                            std::allocator<char> >
+    //   Si # ::std::basic_istream<char,  std::char_traits<char> >
+    //   So # ::std::basic_ostream<char,  std::char_traits<char> >
+    //   Sd # ::std::basic_iostream<char, std::char_traits<char> >
+    //
+    // <seq-id> ::=
+    //   0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z
+    //       [<seq-id>]        # Base 36 number
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_substitution(string_type& output,
+         qualifier_list<Tp, Allocator>* qualifiers)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_substitution");
+       unsigned int value = 0;
+       char c = next();
+       if (c != '_')
+       {
+         switch(c)
+         {
+           case 'a':
+           {
+             output += "std::allocator";
+             if (!M_inside_template_args)
+             {
+               M_function_name = "allocator";
+               M_name_is_template = true;
+               M_name_is_cdtor = false;
+               M_name_is_conversion_operator = false;
+             }
+             eat_current();
+             if (qualifiers)
+               qualifiers->printing_suppressed();
+             _GLIBCXX_DEMANGLER_RETURN;
+           }
+           case 'b':
+           {
+             output += "std::basic_string";
+             if (!M_inside_template_args)
+             {
+               M_function_name = "basic_string";
+               M_name_is_template = true;
+               M_name_is_cdtor = false;
+               M_name_is_conversion_operator = false;
+             }
+             eat_current();
+             if (qualifiers)
+               qualifiers->printing_suppressed();
+             _GLIBCXX_DEMANGLER_RETURN;
+           }
+           case 'd':
+             output += "std::iostream";
+             if (!M_inside_template_args)
+             {
+               M_function_name = "iostream";
+               M_name_is_template = true;
+               M_name_is_cdtor = false;
+               M_name_is_conversion_operator = false;
+             }
+             eat_current();
+             if (qualifiers)
+               qualifiers->printing_suppressed();
+             _GLIBCXX_DEMANGLER_RETURN;
+           case 'i':
+             output += "std::istream";
+             if (!M_inside_template_args)
+             {
+               M_function_name = "istream";
+               M_name_is_template = true;
+               M_name_is_cdtor = false;
+               M_name_is_conversion_operator = false;
+             }
+             eat_current();
+             if (qualifiers)
+               qualifiers->printing_suppressed();
+             _GLIBCXX_DEMANGLER_RETURN;
+           case 'o':
+             output += "std::ostream";
+             if (!M_inside_template_args)
+             {
+               M_function_name = "ostream";
+               M_name_is_template = true;
+               M_name_is_cdtor = false;
+               M_name_is_conversion_operator = false;
+             }
+             eat_current();
+             if (qualifiers)
+               qualifiers->printing_suppressed();
+             _GLIBCXX_DEMANGLER_RETURN;
+           case 's':
+             output += "std::string";
+             if (!M_inside_template_args)
+             {
+               M_function_name = "string";
+               M_name_is_template = true;
+               M_name_is_cdtor = false;
+               M_name_is_conversion_operator = false;
+             }
+             eat_current();
+             if (qualifiers)
+               qualifiers->printing_suppressed();
+             _GLIBCXX_DEMANGLER_RETURN;
+           case 't':
+             output += "std";
+             eat_current();
+             if (qualifiers)
+               qualifiers->printing_suppressed();
+             _GLIBCXX_DEMANGLER_RETURN;
+           default:
+             for(;; c = next())
+             {
+               if (isdigit(c))
+                 value = value * 36 + c - '0';
+               else if (isupper(c))
+                 value = value * 36 + c - 'A' + 10;
+               else if (c == '_')
+                 break;
+               else
+                 _GLIBCXX_DEMANGLER_FAILURE;
+             }
+             ++value;
+             break;
+         }
+       }
+       eat_current();
+       if (value >= M_substitutions_pos.size() ||
+           M_inside_type > 20)                 // Rather than core dump.
+         _GLIBCXX_DEMANGLER_FAILURE;
+       ++M_inside_substitution;
+       int saved_pos = M_pos;
+       substitution_st& substitution(M_substitutions_pos[value]);
+       M_pos = substitution.M_start_pos;
+       switch(substitution.M_type)
+       {
+         case type:
+           decode_type(output, qualifiers);
+           break;
+         case template_template_param:
+           decode_template_param(output, qualifiers);
+           break;
+         case nested_name_prefix:
+         case nested_name_template_prefix:
+           for (int cnt = substitution.M_number_of_prefixes; cnt > 0; --cnt)
+           {
+             if (current() == 'I')
+             {
+               if (M_template_args_need_space)
+                 output += ' ';
+               M_template_args_need_space = false;
+               if (!decode_template_args(output))
+                 _GLIBCXX_DEMANGLER_FAILURE;
+             }
+             else
+             {
+               if (cnt < substitution.M_number_of_prefixes)
+                 output += "::";
+               if (current() == 'S')
+               {
+                 if (!decode_substitution(output))
+                   _GLIBCXX_DEMANGLER_FAILURE;
+               }
+               else if (!decode_unqualified_name(output))
+                 _GLIBCXX_DEMANGLER_FAILURE;
+             }
+           }
+           if (qualifiers)
+             qualifiers->printing_suppressed();
+           break;
+         case unscoped_template_name:
+           decode_unscoped_name(output);
+           if (qualifiers)
+             qualifiers->printing_suppressed();
+           break;
+       }
+       M_pos = saved_pos;
+       --M_inside_substitution;
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <template-param> ::= T_                 # first template parameter
+    //                  ::= T <parameter-2 non-negative number> _
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_template_param(string_type& output,
+         qualifier_list<Tp, Allocator>* qualifiers)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_template_parameter");
+       if (current() != 'T')
+         _GLIBCXX_DEMANGLER_FAILURE;
+       unsigned int value = 0;
+       char c;
+       if ((c = next()) != '_')
+       {
+         while(isdigit(c))
+         {
+           value = value * 10 + c - '0';
+           c = next();
+         }
+         ++value;
+       }
+       if (eat_current() != '_')
+         _GLIBCXX_DEMANGLER_FAILURE;
+       value += M_template_arg_pos_offset;
+       if (value >= M_template_arg_pos.size())
+         _GLIBCXX_DEMANGLER_FAILURE;
+       int saved_pos = M_pos;
+       M_pos = M_template_arg_pos[value];
+       if (M_inside_type > 20)         // Rather than core dump.
+         _GLIBCXX_DEMANGLER_FAILURE;
+       ++M_inside_substitution;
+       if (current() == 'X')
+       {
+         eat_current();
+         decode_expression(output);
+       }
+       else if (current() == 'L')
+         decode_literal(output);
+       else
+         decode_type(output, qualifiers);
+       --M_inside_substitution;
+       M_pos = saved_pos;
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_real(string_type& output, size_t size_of_real)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_real");
+
+       unsigned long words[4]; // 32 bit per long, maximum of 128 bits.
+       unsigned long* word = &words[0];
+
+       int saved_pos;
+       store(saved_pos);
+
+       // The following assumes that leading zeroes are also included in the
+       // mangled name, I am not sure that is conforming to the C++-ABI, but
+       // it is what g++ does.
+       unsigned char nibble, c = current();
+       for(size_t word_cnt = size_of_real / 4; word_cnt > 0; --word_cnt)
+       {
+         for (int nibble_cnt = 0; nibble_cnt < 8; ++nibble_cnt)
+         {
+           // Translate character into nibble.
+           if (c < '0' || c > 'f')
+             _GLIBCXX_DEMANGLER_FAILURE;
+           if (c <= '9')
+             nibble = c - '0';
+           else if (c >= 'a')
+             nibble = c - 'a' + 10;
+           else
+             _GLIBCXX_DEMANGLER_FAILURE;
+           // Write nibble into word array.
+           if (nibble_cnt == 0)
+             *word = nibble << 28;
+           else
+             *word |= (nibble << (28 - 4 * nibble_cnt));
+           c = next();
+         }
+         ++word;
+       }
+       char buf[24];
+       if (M_implementation_details.decode_real(buf, words, size_of_real))
+       {
+         output += buf;
+         _GLIBCXX_DEMANGLER_RETURN;
+       }
+       restore(saved_pos);
+
+       output += '[';
+       c = current();
+       for(size_t nibble_cnt = 0; nibble_cnt < 2 * size_of_real; ++nibble_cnt)
+       {
+         if (c < '0' || c > 'f' || (c > '9' && c < 'a'))
+           _GLIBCXX_DEMANGLER_FAILURE;
+         output += c;
+         c = next();
+       }
+       output += ']';
+
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_literal(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_literal");
+       eat_current();  // Eat the 'L'.
+       if (current() == '_')
+       {
+         if (next() != 'Z')
+           _GLIBCXX_DEMANGLER_FAILURE;
+         eat_current();
+         if ((M_pos += decode_encoding(output, M_str + M_pos,
+                 M_maxpos - M_pos + 1, M_implementation_details)) < 0)
+           _GLIBCXX_DEMANGLER_FAILURE;
+       }
+       else
+       {
+         // Special cases
+         if (current() == 'b')
+         {
+           if (next() == '0')
+             output += "false";
+           else
+             output += "true";
+           eat_current();
+           _GLIBCXX_DEMANGLER_RETURN;
+         }
+         char c = current();
+         if ((c == 'i' || c == 'j' || c == 'l' ||
+              c == 'm' || c == 'x' || c == 'y') &&
+              M_implementation_details.get_style_literal())
+           eat_current();
+         else if (c == 'i' &&
+             !M_implementation_details.get_style_literal_int())
+           eat_current();
+         else
+         {
+           output += '(';
+           if (!decode_type(output))
+             _GLIBCXX_DEMANGLER_FAILURE;
+           output += ')';
+         }
+         if (c >= 'd' && c <= 'g')
+         {
+           size_t size_of_real = (c == 'd') ? sizeof(double) :
+               ((c == 'f') ? sizeof(float) :
+               (c == 'e') ?  sizeof(long double) : 16);
+           if (!decode_real(output, size_of_real))
+               _GLIBCXX_DEMANGLER_FAILURE;
+         }
+         else if (!decode_number(output))
+           _GLIBCXX_DEMANGLER_FAILURE;
+          if (M_implementation_details.get_style_literal())
+         {
+           if (c == 'j' || c == 'm' || c == 'y')
+             output += 'u';
+           if (c == 'l' || c == 'm')
+             output += 'l';
+           if (c == 'x' || c == 'y')
+             output += "ll";
+         }
+       }
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <operator-name> ::=
+    //   nw                            # new
+    //   na                            # new[]
+    //   dl                            # delete
+    //   da                            # delete[]
+    //   ps                            # + (unary)
+    //   ng                            # - (unary)
+    //   ad                            # & (unary)
+    //   de                            # * (unary)
+    //   co                            # ~
+    //   pl                            # +
+    //   mi                            # -
+    //   ml                            # *
+    //   dv                            # /
+    //   rm                            # %
+    //   an                            # &
+    //   or                            # |
+    //   eo                            # ^
+    //   aS                            # =
+    //   pL                            # +=
+    //   mI                            # -=
+    //   mL                            # *=
+    //   dV                            # /=
+    //   rM                            # %=
+    //   aN                            # &=
+    //   oR                            # |=
+    //   eO                            # ^=
+    //   ls                            # <<
+    //   rs                            # >>
+    //   lS                            # <<=
+    //   rS                            # >>=
+    //   eq                            # ==
+    //   ne                            # !=
+    //   lt                            # <
+    //   gt                            # >
+    //   le                            # <=
+    //   ge                            # >=
+    //   nt                            # !
+    //   aa                            # &&
+    //   oo                            # ||
+    //   pp                            # ++
+    //   mm                            # --
+    //   cm                            # ,
+    //   pm                            # ->*
+    //   pt                            # ->
+    //   cl                            # ()
+    //   ix                            # []
+    //   qu                            # ?
+    //   st                            # sizeof (a type)
+    //   sz                            # sizeof (an expression)
+    //   cv <type>                     # (cast)
+    //   v <digit> <source-name>       # vendor extended operator
+    //
+    // Symbol operator codes exist of two characters, we need to find a
+    // quick hash so that their names can be looked up in a table.
+    //
+    // The puzzle :)
+    // Shift the rows so that there is at most one character per column.
+    //
+    // A perfect solution (Oh no, it's THE MATRIX!):
+    //                                              horizontal
+    //    .......................................   offset + 'a'
+    // a, a||d|||||||||n||||s||||||||||||||||||||       0
+    // c,  || |||||||lm o||| ||||||||||||||||||||       0
+    // d,  || a|||e||    l|| ||||||v|||||||||||||       4
+    // e,  ||  ||| ||     || |||o|q |||||||||||||       8
+    // g,  ||  ||| ||     || e|| |  ||||||||t||||      15
+    // i,  ||  ||| ||     ||  || |  |||||||| |||x      15
+    // l,  |e  ||| ||     st  || |  |||||||| |||       -2
+    // m,  |   |i| lm         || |  |||||||| |||       -2
+    // n,  a   e g            t| w  |||||||| |||        1
+    // o,                      |    ||||o||r |||       16
+    // p,                      |    ||lm |p  st|       17
+    // q,                      |    u|   |     |        6
+    // r,                      m     s   |     |        9
+    // s,                                t     z       12
+    //    .......................................
+    // ^            ^__ second character
+    // |___ first character
+    //
+
+    // Putting that solution in tables:
+
+    char const offset_table_c [1 + CHAR_MAX - CHAR_MIN ] =
+    {
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+#if (CHAR_MIN < 0)
+      // Add -CHAR_MIN extra zeroes (128):
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      //   a    b    c    d    e    f    g    h    i    j    k
+      0, -97,   0, -97, -93, -89,   0, -82,   0, -82,   0,   0,
+      //   l    m    n    o    p    q    r    s    t    u    v
+        -99, -99, -96, -81, -80, -91, -88, -85,   0,   0,   0,
+#else
+      //   a    b    c    d    e    f    g    h    i    j    k
+      0, 159,   0, 159, 163, 167,   0, 174,   0, 174,   0,   0,
+      //   l    m    n    o    p    q    r    s    t    u    v
+        157, 157, 160, 175, 176, 165, 168, 171,   0,   0,   0,
+#endif
+      // ... more zeros
+    };
+
+    enum xary_nt {
+      unary,
+      binary,
+      trinary
+    };
+
+    struct entry_st
+    {
+      char const* opcode;
+      char const* symbol_name;
+      xary_nt type;
+    };
+
+    entry_st const symbol_name_table_c[39] = {
+      { "aa",  "operator&&", binary },
+      { "na",  "operator new[]", unary },
+      { "le",  "operator<=", binary },
+      { "ad",  "operator&", unary },
+      { "da",  "operator delete[]", unary },
+      { "ne",  "operator!=", binary },
+      { "mi=", "operator-", binary },
+      { "ng",  "operator-", unary },
+      { "de",  "operator*", unary },
+      { "ml=", "operator*", binary },
+      { "mm",  "operator--", unary },
+      { "cl",  "operator()", unary },
+      { "cm",  "operator,", binary },
+      { "an=", "operator&", binary },
+      { "co",  "operator~", binary },
+      { "dl",  "operator delete", unary },
+      { "ls=", "operator<<", binary },
+      { "lt",  "operator<", binary },
+      { "as=", "operator", binary },
+      { "ge",  "operator>=", binary },
+      { "nt",  "operator!", unary },
+      { "rm=", "operator%", binary },
+      { "eo=", "operator^", binary },
+      { "nw",  "operator new", unary },
+      { "eq",  "operator==", binary },
+      { "dv=", "operator/", binary },
+      { "qu",  "operator?", trinary },
+      { "rs=", "operator>>", binary },
+      { "pl=", "operator+", binary },
+      { "pm",  "operator->*", binary },
+      { "oo",  "operator||", binary },
+      { "st",  "sizeof", unary },
+      { "pp",  "operator++", unary },
+      { "or=", "operator|", binary },
+      { "gt",  "operator>", binary },
+      { "ps",  "operator+", unary },
+      { "pt",  "operator->", binary },
+      { "sz",  "sizeof", unary },
+      { "ix",  "operator[]", unary }
+    };
+
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_operator_name(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_operator_name");
+
+       char opcode0 = current();
+       char opcode1 = tolower(next());
+
+       register char hash;
+       if ((hash = offset_table_c[opcode0 - CHAR_MIN]))
+       {
+         hash += opcode1;
+         if (
+#if (CHAR_MIN < 0)
+             hash >= 0 &&
+#endif
+             hash < 39)
+         {
+           int index = static_cast<int>(static_cast<unsigned char>(hash));
+           entry_st entry = symbol_name_table_c[index];
+           if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1
+               && (opcode1 == current() || entry.opcode[2] == '='))
+           {
+             output += entry.symbol_name;
+             if (opcode1 != current())
+               output += '=';
+             eat_current();
+             if (hash == 16 || hash == 17)
+               M_template_args_need_space = true;
+             _GLIBCXX_DEMANGLER_RETURN;
+           }
+           else if (opcode0 == 'c' && opcode1 == 'v')  // casting operator
+           {
+             eat_current();
+             output += "operator ";
+             if (current() == 'T')
+             {
+               // This is a templated cast operator.
+               // It must be of the form "cvT_I...E".
+               // Let M_template_arg_pos already point
+               // to the template argument.
+               M_template_arg_pos_offset = M_template_arg_pos.size();
+               M_template_arg_pos.push_back(M_pos + 3);
+             }
+             if (!decode_type(output))
+               _GLIBCXX_DEMANGLER_FAILURE;
+             if (!M_inside_template_args)
+               M_name_is_conversion_operator = true;
+             _GLIBCXX_DEMANGLER_RETURN;
+           }
+         }
+       }
+       _GLIBCXX_DEMANGLER_FAILURE;
+      }
+
+    //
+    // <expression> ::= <unary operator-name> <expression>
+    //              ::= <binary operator-name> <expression> <expression>
+    //              ::= <trinary operator-name> <expression> <expression> <expression>
+    //              ::= st <type>
+    //              ::= <template-param>
+    //              ::= sr <type> <unqualified-name>                   # dependent name
+    //              ::= sr <type> <unqualified-name> <template-args>   # dependent template-id
+    //              ::= <expr-primary>
+    //
+    // <expr-primary> ::= L <type> <value number> E     # integer literal
+    //                ::= L <type> <value float> E     # floating literal
+    //                ::= L <mangled-name> E           # external name
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_expression(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_expression");
+       if (current() == 'T')
+       {
+         if (!decode_template_param(output))
+           _GLIBCXX_DEMANGLER_FAILURE;
+         _GLIBCXX_DEMANGLER_RETURN;
+       }
+       else if (current() == 'L')
+       {
+         if (!decode_literal(output))
+           _GLIBCXX_DEMANGLER_FAILURE;
+         if (current() != 'E')
+           _GLIBCXX_DEMANGLER_FAILURE;
+         eat_current();
+         _GLIBCXX_DEMANGLER_RETURN;
+       }
+       else if (current() == 's')
+       {
+         char opcode1 = next();
+         if (opcode1 == 't' || opcode1 == 'z')
+         {
+           eat_current();
+           if (M_implementation_details.get_style_compact_expr_ops())
+             output += "sizeof(";
+           else
+             output += "sizeof (";
+           if (opcode1 == 't')
+           {
+             // I cannot think of a mangled name that is valid for both cases
+             // when just replacing the 't' by a 'z' or vica versa, which
+             // indicates that there is no ambiguity that dictates the need
+             // for a seperate "st" case, except to be able catch invalid
+             // mangled names.  However there CAN be ambiguity in the demangled
+             // name when there are both a type and a symbol of the same name,
+             // which then leads to different encoding (of course) with
+             // sizeof (type) or sizeof (expression) respectively, but that
+             // ambiguity is not per se related to "sizeof" except that that
+             // is the only place where both a type AND an expression are valid
+             // in as part of a (template function) type.
+             //
+             // Example:
+             //
+             // struct B { typedef int t; };
+             // struct A : public B { static int t[2]; };
+             // template<int i, int j> struct C { typedef int q; };
+             // template<int i, typename T>
+             //   void f(typename C<sizeof (typename T::t),
+             //                     sizeof (T::t)>::q) { }
+             // void instantiate() { f<5, A>(0); }
+             //
+             // Leads to _Z1fILi5E1AEvN1CIXstN1T1tEEXszsrS2_1tEE1qE which
+             // demangles as
+             // void f<5, A>(C<sizeof (T::t), sizeof (T::t)>::q)
+             //
+             // This is ambiguity is very unlikely to happen and it is kind
+             // of fuzzy to detect when adding a 'typename' makes sense.
+             //
+             if (M_implementation_details.get_style_sizeof_typename())
+             {
+               // We can only get here inside a template parameter,
+               // so this is syntactically correct if the given type is
+               // a typedef.  The only disadvantage is that it is inconsistent
+               // with all other places where the 'typename' keyword should be
+               // used and we don't.
+               // With this, the above example will demangle as
+               // void f<5, A>(C<sizeof (typename T::t), sizeof (T::t)>::q)
+               if (current() == 'N' || // <nested-name>
+                                         // This should be a safe bet.
+                   (current() == 'S' &&
+                    next_peek() == 't'))       // std::something, guess that
+                                         // this involves a typedef.
+                 output += "typename ";
+             }
+             if (!decode_type(output))
+               _GLIBCXX_DEMANGLER_FAILURE;
+           }
+           else
+           {
+             if (!decode_expression(output))
+               _GLIBCXX_DEMANGLER_FAILURE;
+           }
+           output += ')';
+           _GLIBCXX_DEMANGLER_RETURN;
+         }
+         else if (current() == 'r')
+         {
+           eat_current();
+           if (!decode_type(output))
+             _GLIBCXX_DEMANGLER_FAILURE;
+           output += "::";
+           if (!decode_unqualified_name(output))
+             _GLIBCXX_DEMANGLER_FAILURE;
+           if (current() != 'I' || decode_template_args(output))
+             _GLIBCXX_DEMANGLER_RETURN;
+         }
+       }
+       else
+       {
+         char opcode0 = current();
+         char opcode1 = tolower(next());
+
+         register char hash;
+         if ((hash = offset_table_c[opcode0 - CHAR_MIN]))
+         {
+           hash += opcode1;
+           if (
+#if (CHAR_MIN < 0)
+               hash >= 0 &&
+#endif
+               hash < 39)
+           {
+             int index = static_cast<int>(static_cast<unsigned char>(hash));
+             entry_st entry = symbol_name_table_c[index];
+             if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1
+                 && (opcode1 == current() || entry.opcode[2] == '='))
+             {
+               char const* op = entry.symbol_name + 8; // Skip "operator".
+               if (*op == ' ')                         // operator new and delete.
+                 ++op;
+               if (entry.type == unary)
+                 output += op;
+               bool is_eq = (opcode1 != current());
+               eat_current();
+               if (index == 34 && M_inside_template_args)      // operator>
+                 output += '(';
+               output += '(';
+               if (!decode_expression(output))
+                 _GLIBCXX_DEMANGLER_FAILURE;
+               output += ')';
+               if (entry.type != unary)
+               {
+                 if (!M_implementation_details.get_style_compact_expr_ops())
+                   output += ' ';
+                 output += op;
+                 if (is_eq)
+                   output += '=';
+                 if (!M_implementation_details.get_style_compact_expr_ops())
+                   output += ' ';
+                 output += '(';
+                 if (!decode_expression(output))
+                   _GLIBCXX_DEMANGLER_FAILURE;
+                 output += ')';
+                 if (index == 34 && M_inside_template_args)
+                   output += ')';
+                 if (entry.type == trinary)
+                 {
+                   if (M_implementation_details.get_style_compact_expr_ops())
+                     output += ":(";
+                   else
+                     output += " : (";
+                   if (!decode_expression(output))
+                     _GLIBCXX_DEMANGLER_FAILURE;
+                   output += ')';
+                 }
+               }
+               _GLIBCXX_DEMANGLER_RETURN;
+             }
+             else if (opcode0 == 'c' &&
+                      opcode1 == 'v')          // casting operator.
+             {
+               eat_current();
+               output += '(';
+               if (!decode_type(output))
+                 _GLIBCXX_DEMANGLER_FAILURE;
+               output += ")(";
+               if (!decode_expression(output))
+                 _GLIBCXX_DEMANGLER_FAILURE;
+               output += ')';
+               _GLIBCXX_DEMANGLER_RETURN;
+             }
+           }
+         }
+       }
+       _GLIBCXX_DEMANGLER_FAILURE;
+      }
+
+    //
+    // <template-args> ::= I <template-arg>+ E
+    // <template-arg> ::= <type>                       # type or template
+    //                ::= L <type> <value number> E    # integer literal
+    //                ::= L <type> <value float> E     # floating literal
+    //                ::= L <mangled-name> E           # external name
+    //                ::= X <expression> E             # expression
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_template_args(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_template_args");
+       if (eat_current() != 'I')
+         _GLIBCXX_DEMANGLER_FAILURE;
+       int prev_size = M_template_arg_pos.size();
+       ++M_inside_template_args;
+       if (M_template_args_need_space)
+       {
+         output += ' ';
+         M_template_args_need_space = false;
+       }
+       output += '<';
+       for(;;)
+       {
+         if (M_inside_template_args == 1 && !M_inside_type)
+           M_template_arg_pos.push_back(M_pos);
+         if (current() == 'X')
+         {
+           eat_current();
+           if (!decode_expression(output))
+             _GLIBCXX_DEMANGLER_FAILURE;
+           if (current() != 'E')
+             _GLIBCXX_DEMANGLER_FAILURE;
+           eat_current();
+         }
+         else if (current() == 'L')
+         {
+           if (!decode_literal(output))
+             _GLIBCXX_DEMANGLER_FAILURE;
+           if (current() != 'E')
+             _GLIBCXX_DEMANGLER_FAILURE;
+           eat_current();
+         }
+         else if (!decode_type(output))
+           _GLIBCXX_DEMANGLER_FAILURE;
+         if (current() == 'E')
+           break;
+         output += ", ";
+       }
+       eat_current();
+       if (*(output.rbegin()) == '>')
+         output += ' ';
+       output += '>';
+       --M_inside_template_args;
+       if (!M_inside_template_args && !M_inside_type)
+       {
+         M_name_is_template = true;
+         M_template_arg_pos_offset = prev_size;
+       }
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <bare-function-type> ::=
+    //   <signature type>+             # Types are parameter types.
+    //
+    // Note that the possible return type of the <bare-function-type>
+    // has already been eaten before we call this function.  This makes
+    // our <bare-function-type> slightly different from the one in
+    // the C++-ABI description.
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_bare_function_type(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_bare_function_type");
+       if (M_saw_destructor)
+       {
+         if (eat_current() != 'v' || (current() != 'E' && current() != 0))
+           _GLIBCXX_DEMANGLER_FAILURE;
+         output += "()";
+         M_saw_destructor = false;
+         _GLIBCXX_DEMANGLER_RETURN;
+       }
+       if (current() == 'v' && !M_implementation_details.get_style_void())
+       {
+         eat_current();
+         if (current() != 'E' && current() != 0)
+           _GLIBCXX_DEMANGLER_FAILURE;
+         output += "()";
+         M_saw_destructor = false;
+         _GLIBCXX_DEMANGLER_RETURN;
+       }
+       output += '(';
+       M_template_args_need_space = false;
+       if (!decode_type(output))       // Must have at least one parameter.
+         _GLIBCXX_DEMANGLER_FAILURE;
+       while (current() != 'E' && current() != 0)
+       {
+         output += ", ";
+         if (!decode_type(output))
+           _GLIBCXX_DEMANGLER_FAILURE;
+       }
+       output += ')';
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <type> ::=
+    //   <builtin-type>                # Starts with a lower case character != r.
+    //   <function-type>       # Starts with F
+    //   <class-enum-type>     # Starts with N, S, C, D, Z, a digit or a lower
+    //                         # case character.  Since a lower case character
+    //                         # would be an operator name, that would be an
+    //                         # error.  The S is a substitution or St
+    //                         # (::std::).  A 'C' would be a constructor and
+    //                         # thus also an error.
+    //   <template-param>      # Starts with T
+    //   <substitution>         # Starts with S
+    //   <template-template-param> <template-args>  # Starts with T or S,
+    //                                             # equivalent with the above.
+    //
+    //   <array-type>                  # Starts with A
+    //   <pointer-to-member-type>      # Starts with M
+    //   <CV-qualifiers> <type>                # Starts with r, V or K
+    //   P <type>   # pointer-to       # Starts with P
+    //   R <type>   # reference-to     # Starts with R
+    //   C <type>   # complex (C 2000) # Starts with C
+    //   G <type>   # imaginary (C 2000)# Starts with G
+    //   U <source-name> <type>                # vendor extended type qualifier,
+    //                                 # starts with U
+    //
+    // <template-template-param> ::= <template-param>
+    //                           ::= <substitution>
+
+    // My own analysis of how to decode qualifiers:
+    //
+    // F is a <function-type>, <T> is a <builtin-type>, <class-enum-type>,
+    //   <template-param> or <template-template-param> <template-args>.
+    // <Q> represents a series of qualifiers (not G or C).
+    // <C> is an unqualified type.
+    // <R> is a qualified type.
+    // <B> is the bare-function-type without return type.
+    // <I> is the array index.
+    //                                         Substitutions:
+    // <Q>M<C><Q2>F<R><B>E  ==> R (C::*Q)B Q2  "<C>", "F<R><B>E"
+    //                                             (<R> and <B> recursive),
+    //                                             "M<C><Q2>F<R><B>E".
+    // <Q>F<R><B>E         ==> R (Q)B          "<R>", "<B>" (<B> recursive)
+    //                                              and "F<R><B>E".
+    //
+    // Note that if <R> has postfix qualifiers (an array or function), then
+    // those are added AFTER the (member) function type.  For example:
+    // <Q>FPA<R><B>E ==> R (*(Q)B) [], where the PA added the prefix
+    // "(*" and the postfix ") []".
+    //
+    // <Q>G<T>             ==> imaginary T Q   "<T>", "G<T>" (<T> recursive).
+    // <Q>C<T>             ==> complex T Q     "<T>", "C<T>" (<T> recursive).
+    // <Q><T>              ==> T Q             "<T>" (<T> recursive).
+    //
+    // where <Q> is any of:
+    //
+    // <Q>P            ==> *Q                          "P..."
+    // <Q>R            ==> &Q                          "R..."
+    // <Q>[K|V|r]+     ==> [ const| volatile| restrict]+Q      "KVr..."
+    // <Q>U<S>         ==>  SQ                         "U<S>..."
+    // <Q>M<C>         ==> C::*Q                       "M<C>..." (<C> recurs.)
+    // A<I>            ==>  [I]                        "A<I>..." (<I> recurs.)
+    // <Q>A<I>         ==>  (Q) [I]                    "A<I>..." (<I> recurs.)
+    //   Note that when <Q> ends on an A<I2> then the brackets are omitted
+    //   and no space is written between the two:
+    //   A<I2>A<I>     ==>  [I2][I]
+    //   If <Q> ends on [KVr]+, which can happen in combination with
+    //   substitutions only, then special handling is required, see below.
+    //
+    // A <substitution> is handled with an input position switch during which
+    // new substitutions are turned off.  Because recursive handling of types
+    // (and therefore the order in which substitutions must be generated) must
+    // be done left to right, but the generation of Q needs processing right to
+    // left, substitutions per <type> are generated by reading the input left
+    // to right and marking the starts of all substitutions only - implicitly
+    // finishing them at the end of the type.  Then the output and real
+    // substitutions are generated.
+    //
+    // The following comment was for the demangling of g++ version 3.0.x.  The
+    // mangling (and I believe even the ABI description) have been fixed now
+    // (as of g++ version 3.1).
+    //
+    // g++ 3.0.x only:
+    // The ABI specifies for pointer-to-member function types the format
+    // <Q>M<T>F<R><B>E.  In other words, the qualifier <Q2> (see above) is
+    // implicitely contained in <T> instead of explicitly part of the M format.
+    // I am convinced that this is a bug in the ABI.  Unfortunately, this is
+    // how we have to demangle things as it has a direct impact on the order
+    // in which substitutions are stored.  This ill-formed design results in
+    // rather ill-formed demangler code too however :/
+    //
+    // <Q2> is now explicitely part of the M format.
+    // For some weird reason, g++ (3.2.1) does not add substitutions for
+    // qualified member function pointers.  I think that is another bug.
+    //
+
+    // In the case of
+    // <Q>A<I>
+    // where <Q> ends on [K|V|r]+ then that part should be processed as
+    // if it was behind the A<I> instead of in front of it.  This is
+    // because a constant array of ints is normally always mangled as
+    // an array of constant ints.  KVr qualifiers can end up in front
+    // of an array when the array is part of a substitution or template
+    // parameter, but the demangling should still result in the same
+    // syntax; thus KA2_i (const array of ints) must result in the same
+    // demangling as A2_Ki (array of const ints).  As a result we must
+    // demangle ...[...[[KVr]+A<I0>][KVr]+A<I1>]...[KVr]+A<In>[KVr]+
+    // as A<I0>A<I1>...A<In>[KVr]+ where each K, V and r in the series
+    // collapses to a single character at the right of the string.
+    // For example:
+    // VA9_KrA6_KVi --> A9_A6_KVri --> int volatile const restrict [9][6]
+    // Note that substitutions are still added as usual (the translation
+    // to A9_A6_KVri does not really happen).
+    //
+    // This decoding is achieved by delaying the decoding of any sequence
+    // of [KVrA]'s and processing them together in the order: first the
+    // short-circuited KVr part and then the arrays.
+    static int const cvq_K = 1;                // Saw at least one K
+    static int const cvq_V = 2;                // Saw at least one V
+    static int const cvq_r = 4;                // Saw at least one r
+    static int const cvq_A = 8;                // Saw at least one A
+    static int const cvq_last = 16;    // No remaining qualifiers.
+    static int const cvq_A_cnt = 32;   // Bit 5 and higher represent the
+                                       //   number of A's in the series.
+    // In the function below, iter_array points to the first (right most)
+    // A in the series, if any.
+    template<typename Tp, typename Allocator>
+      void
+      qualifier_list<Tp, Allocator>::decode_KVrA(
+          string_type& prefix, string_type& postfix, int cvq,
+          typename qual_vector::const_reverse_iterator const& iter_array) const
+       {
+         _GLIBCXX_DEMANGLER_DOUT_ENTERING3("decode_KVrA");
+         if ((cvq & cvq_K))
+           prefix += " const";
+         if ((cvq & cvq_V))
+           prefix += " volatile";
+         if ((cvq & cvq_r))
+           prefix += " restrict";
+         if ((cvq & cvq_A))
+         {
+           int n = cvq >> 5;
+           for (typename qual_vector::
+               const_reverse_iterator iter = iter_array;
+               iter != M_qualifier_starts.rend(); ++iter)
+           {
+             switch((*iter).first_qualifier())
+             {
+               case 'K':
+               case 'V':
+               case 'r':
+                 break;
+               case 'A':
+               {
+                 string_type index = (*iter).get_optional_type();
+                 if (--n == 0 && (cvq & cvq_last))
+                   postfix = " [" + index + "]" + postfix;
+                 else if (n > 0)
+                   postfix = "[" + index + "]" + postfix;
+                 else
+                 {
+                   prefix += " (";
+                   postfix = ") [" + index + "]" + postfix;
+                 }
+                 break;
+               }
+               default:
+                 _GLIBCXX_DEMANGLER_RETURN3;
+             }
+           }
+         }
+         _GLIBCXX_DEMANGLER_RETURN3;
+       }
+
+    template<typename Tp, typename Allocator>
+      void
+      qualifier_list<Tp, Allocator>::decode_qualifiers(
+         string_type& prefix,
+         string_type& postfix,
+         bool member_function_pointer_qualifiers = false) const
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING3("decode_qualifiers");
+       int cvq = 0;
+       typename qual_vector::const_reverse_iterator iter_array;
+       for(typename qual_vector::
+           const_reverse_iterator iter = M_qualifier_starts.rbegin();
+           iter != M_qualifier_starts.rend(); ++iter)
+       {
+         if (!member_function_pointer_qualifiers
+             && !(*iter).part_of_substitution())
+         {
+           int saved_inside_substitution = M_demangler.M_inside_substitution;
+           M_demangler.M_inside_substitution = 0;
+           M_demangler.add_substitution((*iter).get_start_pos(), type);
+           M_demangler.M_inside_substitution = saved_inside_substitution;
+         }
+         char qualifier_char = (*iter).first_qualifier();
+         for(; qualifier_char; qualifier_char = (*iter).next_qualifier())
+         {
+           switch(qualifier_char)
+           {
+             case 'P':
+               if (cvq)
+               {
+                 decode_KVrA(prefix, postfix, cvq, iter_array);
+                 cvq = 0;
+               }
+               prefix += "*";
+               break;
+             case 'R':
+               if (cvq)
+               {
+                 decode_KVrA(prefix, postfix, cvq, iter_array);
+                 cvq = 0;
+               }
+               prefix += "&";
+               break;
+             case 'K':
+               cvq |= cvq_K;
+               continue;
+             case 'V':
+               cvq |= cvq_V;
+               continue;
+             case 'r':
+               cvq |= cvq_r;
+               continue;
+             case 'A':
+               if (!(cvq & cvq_A))
+               {
+                 cvq |= cvq_A;
+                 iter_array = iter;
+               }
+               cvq += cvq_A_cnt;
+               break;
+             case 'M':
+               if (cvq)
+               {
+                 decode_KVrA(prefix, postfix, cvq, iter_array);
+                 cvq = 0;
+               }
+               prefix += " ";
+               prefix += (*iter).get_optional_type();
+               prefix += "::*";
+               break;
+             case 'U':
+               if (cvq)
+               {
+                 decode_KVrA(prefix, postfix, cvq, iter_array);
+                 cvq = 0;
+               }
+               prefix += " ";
+               prefix += (*iter).get_optional_type();
+               break;
+             case 'G': // Only here so we added a substitution.
+               break;
+           }
+           break;
+         }
+       }
+       if (cvq)
+         decode_KVrA(prefix, postfix, cvq|cvq_last, iter_array);
+       M_printing_suppressed = false;
+       _GLIBCXX_DEMANGLER_RETURN3;
+      }
+
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_type_with_postfix(
+         string_type& prefix, string_type& postfix,
+         qualifier_list<Tp, Allocator>* qualifiers)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING2("decode_type");
+       ++M_inside_type;
+       bool recursive_template_param_or_substitution_call;
+       if (!(recursive_template_param_or_substitution_call = qualifiers))
+       {
+          qualifier_list<Allocator>* raw_qualifiers = M_qualifier_list_alloc.allocate(1);
+         qualifiers = new (raw_qualifiers) qualifier_list<Allocator>(*this);
+       }
+       // First eat all qualifiers.
+       bool failure = false;
+       for(;;)         // So we can use 'continue' to eat the next qualifier.
+       {
+         int start_pos = M_pos;
+         switch(current())
+         {
+           case 'P':
+             qualifiers->add_qualifier_start(pointer, start_pos,
+                 M_inside_substitution);
+             eat_current();
+             continue;
+           case 'R':
+             qualifiers->add_qualifier_start(reference, start_pos,
+                 M_inside_substitution);
+             eat_current();
+             continue;
+           case 'K':
+           case 'V':
+           case 'r':
+           {
+             char c;
+             int count = 0;
+             do
+             {
+               ++count;
+               c = next();
+             }
+             while(c == 'K' || c == 'V' || c == 'r');
+             qualifiers->add_qualifier_start(cv_qualifier, start_pos, count,
+                 M_inside_substitution);
+             continue;
+           }
+           case 'U':
+           {
+             eat_current();
+             string_type source_name;
+             if (!decode_source_name(source_name))
+             {
+               failure = true;
+               break;
+             }
+             qualifiers->add_qualifier_start(vendor_extension, start_pos,
+                 source_name, M_inside_substitution);
+             continue;
+           }
+           case 'A':
+           {
+             // <array-type> ::= A <positive dimension number> _ <element type>
+             //              ::= A [<dimension expression>] _ <element type>
+             //
+             string_type index;
+             int saved_pos;
+             store(saved_pos);
+             if (next() == 'n' || !decode_number(index))
+             {
+               restore(saved_pos);
+               if (next() != '_' && !decode_expression(index))
+               {
+                 failure = true;
+                 break;
+               }
+             }
+             if (eat_current() != '_')
+             {
+               failure = true;
+               break;
+             }
+             qualifiers->add_qualifier_start(array, start_pos, index,
+                 M_inside_substitution);
+             continue;
+           }
+           case 'M':
+           {
+             // <pointer-to-member-type> ::= M <class type> <member type>
+             // <Q>M<C> or <Q>M<C><Q2>F<R><B>E
+             eat_current();
+             string_type class_type;
+             if (!decode_type(class_type))             // Substitution: "<C>".
+             {
+               failure = true;
+               break;
+             }
+             char c = current();
+             if (c == 'F' || c == 'K' || c == 'V' || c == 'r')
+                 // Must be CV-qualifiers and a member function pointer.
+             {
+               // <Q>M<C><Q2>F<R><B>E  ==> R (C::*Q)B Q2
+               //     substitutions: "<C>", "F<R><B>E" (<R> and <B>
+               //                    recursive), "M<C><Q2>F<R><B>E".
+               int count = 0;
+               int Q2_start_pos = M_pos;
+               while(c == 'K' || c == 'V' || c == 'r')         // Decode <Q2>.
+               {
+                 ++count;
+                 c = next();
+               }
+               qualifier_list<Tp, Allocator> class_type_qualifiers(*this);
+               if (count)
+                 class_type_qualifiers.
+                     add_qualifier_start(cv_qualifier, Q2_start_pos,
+                         count, M_inside_substitution);
+               string_type member_function_qualifiers;
+               // It is unclear why g++ doesn't add a substitution for
+               // "<Q2>F<R><B>E" as it should I think.
+               string_type member_function_qualifiers_postfix;
+               class_type_qualifiers.
+                   decode_qualifiers(member_function_qualifiers,
+                       member_function_qualifiers_postfix, true);
+               member_function_qualifiers +=
+                   member_function_qualifiers_postfix;
+               // I don't think this substitution is actually ever used.
+               int function_pos = M_pos;
+               if (eat_current() != 'F')
+               {
+                 failure = true;
+                 break;
+               }
+               // Return type.
+               // Constructors, destructors and conversion operators don't
+               // have a return type, but seem to never get here.
+               string_type return_type_postfix;
+               if (!decode_type_with_postfix(prefix, return_type_postfix))
+                   // substitution: <R> recursive
+               {
+                 failure = true;
+                 break;
+               }
+               prefix += " (";
+               prefix += class_type;
+               prefix += "::*";
+               string_type bare_function_type;
+               if (!decode_bare_function_type(bare_function_type)
+                   || eat_current() != 'E')    // Substitution: <B> recursive.
+               {
+                 failure = true;
+                 break;
+               }
+               // substitution: "F<R><B>E".
+               add_substitution(function_pos, type);
+               // substitution: "M<C><Q2>F<R><B>E".
+               add_substitution(start_pos, type);
+               // substitution: all qualified types if any.
+               qualifiers->decode_qualifiers(prefix, postfix);
+               postfix += ")";
+               postfix += bare_function_type;
+               postfix += member_function_qualifiers;
+               postfix += return_type_postfix;
+               goto decode_type_exit;
+             }
+             qualifiers->add_qualifier_start(pointer_to_member, start_pos,
+                 class_type, M_inside_substitution);
+             continue;
+           }
+           default:
+             break;
+         }
+         break;
+       }
+       if (!failure)
+       {
+         // <Q>G<T>                    ==> imaginary T Q
+         //     substitutions: "<T>", "G<T>" (<T> recursive).
+         // <Q>C<T>                    ==> complex T Q
+         //     substitutions: "<T>", "C<T>" (<T> recursive).
+         if (current() == 'C' || current() == 'G')
+         {
+           prefix += current() == 'C' ? "complex " : "imaginary ";
+           qualifiers->add_qualifier_start(complex_or_imaginary, M_pos,
+               M_inside_substitution);
+           eat_current();
+         }
+         int start_pos = M_pos;
+         switch(current())
+         {
+           case 'F':
+           {
+             // <function-type> ::= F [Y] <bare-function-type> E
+             //
+             // Note that g++ never generates the 'Y', but we try to
+             // demangle it anyway.
+             bool extern_C = (next() == 'Y');
+             if (extern_C)
+               eat_current();
+
+             // <Q>F<R><B>E            ==> R (Q)B
+             //     substitution: "<R>", "<B>" (<B> recursive) and "F<R><B>E".
+
+             // Return type.
+             string_type return_type_postfix;
+             if (!decode_type_with_postfix(prefix, return_type_postfix))
+                 // Substitution: "<R>".
+             {
+               failure = true;
+               break;
+             }
+             // Only array and function (pointer) types have a postfix.
+             // In that case we don't want the space but expect something
+             // like prefix is "int (*" and postfix is ") [1]".
+             // We do want the space if this pointer is qualified.
+             if (return_type_postfix.size() == 0 ||
+                 (prefix.size() > 0 && *prefix.rbegin() != '*'))
+               prefix += ' ';
+             prefix += '(';
+             string_type bare_function_type;
+             if (!decode_bare_function_type(bare_function_type)
+                 // substitution: "<B>" (<B> recursive).
+                 || eat_current() != 'E')
+             {
+               failure = true;
+               break;
+             }
+             add_substitution(start_pos, type);  // Substitution: "F<R><B>E".
+             qualifiers->decode_qualifiers(prefix, postfix);
+                 // substitution: all qualified types, if any.
+             postfix += ")";
+             if (extern_C)
+               postfix += " [extern \"C\"] ";
+             postfix += bare_function_type;
+             postfix += return_type_postfix;
+             break;
+           }
+           case 'T':
+             if (!decode_template_param(prefix, qualifiers))
+             {
+               failure = true;
+               break;
+             }
+             if (current() == 'I')
+             {
+               add_substitution(start_pos, template_template_param);
+                   // substitution: "<template-template-param>".
+               if (!decode_template_args(prefix))
+               {
+                 failure = true;
+                 break;
+               }
+             }
+             if (!recursive_template_param_or_substitution_call
+                 && qualifiers->suppressed())
+             {
+               add_substitution(start_pos, type);
+                   // substitution: "<template-param>" or
+                   // "<template-template-param> <template-args>".
+               qualifiers->decode_qualifiers(prefix, postfix);
+                   // substitution: all qualified types, if any.
+             }
+             break;
+           case 'S':
+             if (M_pos >= M_maxpos)
+             {
+               failure = true;
+               break;
+             }
+             if (M_str[M_pos + 1] != 't')
+             {
+               if (!decode_substitution(prefix, qualifiers))
+               {
+                 failure = true;
+                 break;
+               }
+               if (current() == 'I')
+               {
+                 if (!decode_template_args(prefix))
+                 {
+                   failure = true;
+                   break;
+                 }
+                 if (!recursive_template_param_or_substitution_call
+                     && qualifiers->suppressed())
+                   add_substitution(start_pos, type);
+                       // Substitution:
+                       //   "<template-template-param> <template-args>".
+               }
+               if (!recursive_template_param_or_substitution_call
+                   && qualifiers->suppressed())
+                 qualifiers->decode_qualifiers(prefix, postfix);
+                     // Substitution: all qualified types, if any.
+               break;
+             }
+             /* Fall-through for St */
+           case 'N':
+           case 'Z':
+           case '0':
+           case '1':
+           case '2':
+           case '3':
+           case '4':
+           case '5':
+           case '6':
+           case '7':
+           case '8':
+           case '9':
+             // <Q><T>                 ==> T Q
+             //     substitutions: "<T>" (<T> recursive).
+             if (!decode_class_enum_type(prefix))
+             {
+               failure = true;
+               break;
+             }
+             if (!recursive_template_param_or_substitution_call)
+             {
+               add_substitution(start_pos, type);
+                   // substitution: "<class-enum-type>".
+               qualifiers->decode_qualifiers(prefix, postfix);
+                   // substitution: all qualified types, if any.
+             }
+             else
+               qualifiers->printing_suppressed();
+             break;
+           default:
+             // <Q><T>                 ==> T Q
+             //     substitutions: "<T>" (<T> recursive).
+             if (!decode_builtin_type(prefix))
+             {
+               failure = true;
+               break;
+             }
+             // If decode_type was called from decode_template_param then we
+             // need to suppress calling qualifiers here in order to get a
+             // substitution added anyway (for the <template-param>).
+             if (!recursive_template_param_or_substitution_call)
+               qualifiers->decode_qualifiers(prefix, postfix);
+             else
+               qualifiers->printing_suppressed();
+             break;
+         }
+       }
+    decode_type_exit:
+       --M_inside_type;
+       if (!recursive_template_param_or_substitution_call)
+       {
+         qualifiers->~qualifier_list<Allocator>();
+         M_qualifier_list_alloc.deallocate(qualifiers, 1);
+       }
+       if (failure)
+         _GLIBCXX_DEMANGLER_FAILURE;
+       _GLIBCXX_DEMANGLER_RETURN2;
+      }
+
+    // <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
+    //               ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
+    //
+    // <prefix> ::= <prefix> <unqualified-name>
+    //          ::= <template-prefix> <template-args>
+    //          ::= <template-param>
+    //          ::= # empty
+    //          ::= <substitution>
+    //
+    // <template-prefix> ::= <prefix> <template unqualified-name>
+    //                   ::= <template-param>
+    //                   ::= <substitution>
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_nested_name(string_type& output,
+                                            string_type& qualifiers)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_nested_name");
+
+       if (current() != 'N' || M_pos >= M_maxpos)
+         _GLIBCXX_DEMANGLER_FAILURE;
+
+       // <CV-qualifiers> ::= [r] [V] [K]  # restrict (C99), volatile, const
+       char const* qualifiers_start = &M_str[M_pos + 1];
+       for (char c = next(); c == 'K' || c == 'V' || c == 'r'; c = next());
+       for (char const* qualifier_ptr = &M_str[M_pos - 1];
+            qualifier_ptr >= qualifiers_start; --qualifier_ptr)
+         switch(*qualifier_ptr)
+         {
+           case 'K':
+             qualifiers += " const";
+             break;
+           case 'V':
+             qualifiers += " volatile";
+             break;
+           case 'r':
+             qualifiers += " restrict";
+             break;
+         }
+
+       int number_of_prefixes = 0;
+       int substitution_start = M_pos;
+       for(;;)
+       {
+         ++number_of_prefixes;
+         if (current() == 'S')
+         {
+           if (!decode_substitution(output))
+             _GLIBCXX_DEMANGLER_FAILURE;
+         }
+         else if (current() == 'I')
+         {
+           if (!decode_template_args(output))
+             _GLIBCXX_DEMANGLER_FAILURE;
+           if (current() != 'E')
+           {
+             // substitution: "<template-prefix> <template-args>".
+             add_substitution(substitution_start, nested_name_prefix,
+                              number_of_prefixes);
+           }
+         }
+         else
+         {
+           if (current() == 'T')
+           {
+             if (!decode_template_param(output))
+               _GLIBCXX_DEMANGLER_FAILURE;
+           }
+           else if (!decode_unqualified_name(output))
+             _GLIBCXX_DEMANGLER_FAILURE;
+           if (current() != 'E')
+           {
+             // substitution: "<prefix> <unqualified-name>" or
+             // "<prefix> <template unqualified-name>".
+             add_substitution(substitution_start,
+                 (current() == 'I') ?  nested_name_template_prefix
+                                    : nested_name_prefix,
+                 number_of_prefixes);
+           }
+         }
+         if (current() == 'E')
+         {
+           eat_current();
+           _GLIBCXX_DEMANGLER_RETURN;
+         }
+         if (current() != 'I')
+           output += "::";
+         else if (M_template_args_need_space)
+           output += ' ';
+         M_template_args_need_space = false;
+       }
+       _GLIBCXX_DEMANGLER_FAILURE;
+      }
+
+    // <local-name> := Z <function encoding> E <entity name> [<discriminator>]
+    //              := Z <function encoding> E s [<discriminator>]
+    // <discriminator> := _ <non-negative number>
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_local_name(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_local_name");
+       if (current() != 'Z' || M_pos >= M_maxpos)
+         _GLIBCXX_DEMANGLER_FAILURE;
+       if ((M_pos += decode_encoding(output, M_str + M_pos + 1,
+               M_maxpos - M_pos, M_implementation_details) + 1) < 0 ||
+               eat_current() != 'E')
+         _GLIBCXX_DEMANGLER_FAILURE;
+       output += "::";
+       if (current() == 's')
+       {
+         eat_current();
+         output += "string literal";
+       }
+       else
+       {
+         string_type nested_name_qualifiers;
+         if (!decode_name(output, nested_name_qualifiers))
+           _GLIBCXX_DEMANGLER_FAILURE;
+         output += nested_name_qualifiers;
+       }
+       string_type discriminator;
+       if (current() == '_' && next() != 'n' && !decode_number(discriminator))
+         _GLIBCXX_DEMANGLER_FAILURE;
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <source-name> ::= <positive length number> <identifier>
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_source_name(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_source_name");
+       int length = current() - '0';
+       if (length < 1 || length > 9)
+         _GLIBCXX_DEMANGLER_FAILURE;
+       while(isdigit(next()))
+         length = 10 * length + current() - '0';
+       char const* ptr = &M_str[M_pos];
+       if (length > 11 && !strncmp(ptr, "_GLOBAL_", 8) && ptr[9] == 'N'
+           && ptr[8] == ptr[10])
+       {
+         output += "(anonymous namespace)";
+         if ((M_pos += length) > M_maxpos + 1)
+           _GLIBCXX_DEMANGLER_FAILURE;
+       }
+       else
+         while(length--)
+         {
+           if (current() == 0)
+             _GLIBCXX_DEMANGLER_FAILURE;
+           output += eat_current();
+         }
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <unqualified-name> ::= <operator-name>  # Starts with lower case.
+    //                    ::= <ctor-dtor-name>  # Starts with 'C' or 'D'.
+    //                    ::= <source-name>    # Starts with a digit.
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_unqualified_name(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_unqualified_name");
+       if (M_inside_template_args)
+       {
+         if (!decode_source_name(output))
+           _GLIBCXX_DEMANGLER_FAILURE;
+       }
+       else if (isdigit(current()))
+       {
+         bool recursive_unqualified_name = (&M_function_name == &output);
+         // This can be a recursive call when we are decoding
+         // an <operator-name> that is a cast operator for a some
+         // <unqualified-name>; for example "operator Foo()".
+         // In that case this is thus not a ctor or dtor and we
+         // are not interested in updating M_function_name.
+         if (!recursive_unqualified_name)
+           M_function_name.clear();
+         M_name_is_template = false;
+         M_name_is_cdtor = false;
+         M_name_is_conversion_operator = false;
+         if (!decode_source_name(M_function_name))
+           _GLIBCXX_DEMANGLER_FAILURE;
+         if (!recursive_unqualified_name)
+           output += M_function_name;
+       }
+       else if (islower(current()))
+       {
+         M_function_name.clear();
+         M_name_is_template = false;
+         M_name_is_cdtor = false;
+         M_name_is_conversion_operator = false;
+         if (!decode_operator_name(M_function_name))
+           _GLIBCXX_DEMANGLER_FAILURE;
+         output += M_function_name;
+       }
+       else if (current() == 'C' || current() == 'D')
+       {
+         // <ctor-dtor-name> ::=
+         //   C1       # complete object (in-charge) constructor
+         //   C2       # base object (not-in-charge) constructor
+         //   C3       # complete object (in-charge) allocating constructor
+         //   D0       # deleting (in-charge) destructor
+         //   D1       # complete object (in-charge) destructor
+         //   D2       # base object (not-in-charge) destructor
+         //
+         if (current() == 'C')
+         {
+           char c = next();
+           if (c < '1' || c > '3')
+             _GLIBCXX_DEMANGLER_FAILURE;
+         }
+         else
+         {
+           char c = next();
+           if (c < '0' || c > '2')
+             _GLIBCXX_DEMANGLER_FAILURE;
+           output += '~';
+           M_saw_destructor = true;
+         }
+         M_name_is_cdtor = true;
+         eat_current();
+         output += M_function_name;
+       }
+       else
+         _GLIBCXX_DEMANGLER_FAILURE;
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <unscoped-name> ::=
+    //   <unqualified-name>            # Starts not with an 'S'
+    //   St <unqualified-name>         # ::std::
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_unscoped_name(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_unscoped_name");
+       if (current() == 'S')
+       {
+         if (next() != 't')
+           _GLIBCXX_DEMANGLER_FAILURE;
+         eat_current();
+         output += "std::";
+       }
+       decode_unqualified_name(output);
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <name> ::=
+    //   <nested-name>                         # Starts with 'N'
+    //   <unscoped-template-name> <template-args> # idem
+    //   <local-name>                          # Starts with 'Z'
+    //   <unscoped-name>                       # Starts with 'S', 'C', 'D',
+    //                                         # a digit or a lower case
+    //                                         # character.
+    //
+    // <unscoped-template-name> ::= <unscoped-name>
+    //                          ::= <substitution>
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_name(string_type& output,
+                                     string_type& nested_name_qualifiers)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_name");
+       int substitution_start = M_pos;
+       if (current() == 'S' && (M_pos >= M_maxpos || M_str[M_pos + 1] != 't'))
+       {
+         if (!decode_substitution(output))
+           _GLIBCXX_DEMANGLER_FAILURE;
+       }
+       else if (current() == 'N')
+       {
+         decode_nested_name(output, nested_name_qualifiers);
+         _GLIBCXX_DEMANGLER_RETURN;
+       }
+       else if (current() == 'Z')
+       {
+         decode_local_name(output);
+         _GLIBCXX_DEMANGLER_RETURN;
+       }
+       else if (!decode_unscoped_name(output))
+         _GLIBCXX_DEMANGLER_FAILURE;
+       if (current() == 'I')
+       {
+         // Must have been an <unscoped-template-name>.
+         add_substitution(substitution_start, unscoped_template_name);
+         if (!decode_template_args(output))
+           _GLIBCXX_DEMANGLER_FAILURE;
+       }
+       M_template_args_need_space = false;
+       _GLIBCXX_DEMANGLER_RETURN;
+      }
+
+    // <call-offset> ::= h <nv-offset> _
+    //               ::= v <v-offset> _
+    // <nv-offset>   ::= <offset number>
+    //     non-virtual base override
+    //
+    // <v-offset>    ::= <offset number> _ <virtual offset number>
+    //     virtual base override, with vcall offset
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_call_offset(string_type&
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+         output
+#endif
+         )
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_call_offset");
+       if (current() == 'h')
+       {
+         string_type dummy;
+         eat_current();
+         if (decode_number(dummy) && current() == '_')
+         {
+           eat_current();
+           _GLIBCXX_DEMANGLER_RETURN;
+         }
+       }
+       else if (current() == 'v')
+       {
+         string_type dummy;
+         eat_current();
+         if (decode_number(dummy) && current() == '_')
+         {
+           eat_current();
+           if (decode_number(dummy) && current() == '_')
+           {
+             eat_current();
+             _GLIBCXX_DEMANGLER_RETURN;
+           }
+         }
+       }
+       _GLIBCXX_DEMANGLER_FAILURE;
+      }
+
+    //
+    // <special-name> ::=
+    //   TV <type>                     # virtual table
+    //   TT <type>                     # VTT structure (construction
+    //                                    vtable index).
+    //   TI <type>                     # typeinfo structure
+    //   TS <type>                     # typeinfo name (null-terminated
+    //                                    byte string).
+    //   GV <object name>              # Guard variable for one-time
+    //                                   initialization of static objects in
+    //                                   a local scope.
+    //   T <call-offset> <base encoding># base is the nominal target function
+    //                                   of thunk.
+    //   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
+    //
+    template<typename Tp, typename Allocator>
+      bool
+      session<Tp, Allocator>::decode_special_name(string_type& output)
+      {
+       _GLIBCXX_DEMANGLER_DOUT_ENTERING("decode_special_name");
+       if (current() == 'G')
+       {
+         if (next() != 'V')
+           _GLIBCXX_DEMANGLER_FAILURE;
+         output += "guard variable for ";
+         string_type nested_name_qualifiers;
+         eat_current();
+         if (!decode_name(output, nested_name_qualifiers))
+           _GLIBCXX_DEMANGLER_FAILURE;
+         output += nested_name_qualifiers;
+         _GLIBCXX_DEMANGLER_RETURN;
+       }
+       else if (current() != 'T')
+         _GLIBCXX_DEMANGLER_FAILURE;
+       switch(next())
+       {
+         case 'V':
+           output += "vtable for ";
+           eat_current();
+           decode_type(output);
+           _GLIBCXX_DEMANGLER_RETURN;
+         case 'T':
+           output += "VTT for ";
+           eat_current();
+           decode_type(output);
+           _GLIBCXX_DEMANGLER_RETURN;
+         case 'I':
+           output += "typeinfo for ";
+           eat_current();
+           decode_type(output);
+           _GLIBCXX_DEMANGLER_RETURN;
+         case 'S':
+           output += "typeinfo name for ";
+           eat_current();
+           decode_type(output);
+           _GLIBCXX_DEMANGLER_RETURN;
+         case 'c':
+           output += "covariant return thunk to ";
+           if (!decode_call_offset(output)
+               || !decode_call_offset(output)
+               || (M_pos += decode_encoding(output, M_str + M_pos,
+                   M_maxpos - M_pos + 1, M_implementation_details)) < 0)
+             _GLIBCXX_DEMANGLER_FAILURE;
+           _GLIBCXX_DEMANGLER_RETURN;
+         case 'C':             // GNU extension?
+         {
+           string_type first;
+           output += "construction vtable for ";
+           eat_current();
+           if (!decode_type(first))
+             _GLIBCXX_DEMANGLER_FAILURE;
+           while(isdigit(current()))
+             eat_current();
+           if (eat_current() != '_')
+             _GLIBCXX_DEMANGLER_FAILURE;
+           if (!decode_type(output))
+             _GLIBCXX_DEMANGLER_FAILURE;
+           output += "-in-";
+           output += first;
+           _GLIBCXX_DEMANGLER_RETURN;
+         }
+         default:
+           if (current() == 'v')
+             output += "virtual thunk to ";
+           else
+             output += "non-virtual thunk to ";
+           if (!decode_call_offset(output)
+               || (M_pos += decode_encoding(output, M_str + M_pos,
+                   M_maxpos - M_pos + 1, M_implementation_details)) < 0)
+             _GLIBCXX_DEMANGLER_FAILURE;
+           _GLIBCXX_DEMANGLER_RETURN;
+       }
+      }
+
+    // <encoding> ::=
+    //   <function name> <bare-function-type>  # Starts with 'C', 'D', 'N',
+    //                                           'S', a digit or a lower case
+    //                                           character.
+    //   <data name>                           # Idem.
+    //   <special-name>                                # Starts with 'T' or 'G'.
+    template<typename Tp, typename Allocator>
+      int
+      session<Tp, Allocator>::decode_encoding(string_type& output,
+          char const* in, int len, implementation_details const& id)
+      {
+#if _GLIBCXX_DEMANGLER_CWDEBUG
+       _GLIBCXX_DEMANGLER_DOUT(dc::demangler,
+           "Output thus far: \"" << output << '"');
+       string_type input(in, len > 0x40000000 ? strlen(in) : len);
+       _GLIBCXX_DEMANGLER_DOUT(
+           dc::demangler, "Entering decode_encoding(\"" << input << "\")");
+#endif
+       if (len <= 0)
+         return INT_MIN;
+       session<Tp, Allocator> demangler_session(in, len, id);
+       string_type nested_name_qualifiers;
+       int saved_pos;
+       demangler_session.store(saved_pos);
+       if (demangler_session.decode_special_name(output))
+         return demangler_session.M_pos;
+       demangler_session.restore(saved_pos);
+       string_type name;
+       if (!demangler_session.decode_name(name, nested_name_qualifiers))
+         return INT_MIN;
+       if (demangler_session.current() == 0
+           || demangler_session.current() == 'E')
+       {
+         output += name;
+         output += nested_name_qualifiers;
+         return demangler_session.M_pos;
+       }
+       // Must have been a <function name>.
+       string_type return_type_postfix;
+       if (demangler_session.M_name_is_template
+           && !(demangler_session.M_name_is_cdtor
+                || demangler_session.M_name_is_conversion_operator))
+       {
+         // Return type of function
+         if (!demangler_session.decode_type_with_postfix(output,
+             return_type_postfix))
+           return INT_MIN;
+         output += ' ';
+       }
+       output += name;
+       if (!demangler_session.decode_bare_function_type(output))
+         return INT_MIN;
+       output += nested_name_qualifiers;
+       output += return_type_postfix;
+       return demangler_session.M_pos;
+      }
+
+    } // namespace demangler
+
+  // Public interface
+  template<typename Tp, typename Allocator>
+    struct demangle
+    {
+      typedef typename Allocator::template rebind<char>::other char_Allocator;
+      typedef std::basic_string<char, std::char_traits<char>, char_Allocator>
+         string_type;
+      static string_type symbol(char const* in,
+                                demangler::implementation_details const& id);
+      static string_type type(char const* in,
+                              demangler::implementation_details const& id);
+    };
+
+  // demangle::symbol()
+  //
+  // Demangle `input' which should be a mangled function name as for
+  // instance returned by nm(1).
+  template<typename Tp, typename Allocator>
+    typename demangle<Tp, Allocator>::string_type
+    demangle<Tp, Allocator>::symbol(char const* input,
+                                demangler::implementation_details const& id)
+    {
+      // <mangled-name> ::= _Z <encoding>
+      // <mangled-name> ::= _GLOBAL_ _<type>_ <disambiguation part>
+      //                    <type> can be I or D (GNU extension)
+      typedef demangler::session<Tp, Allocator> demangler_type;
+      string_type result;
+      bool failure = (input[0] != '_');
+
+      if (!failure)
+      {
+       if (input[1] == 'G')
+       {
+         if (!strncmp(input, "_GLOBAL__", 9)
+             && (input[9] == 'D' || input[9] == 'I')
+             && input[10] == '_')
+         {
+           if (input[9] == 'D')
+             result.assign("global destructors keyed to ", 28);
+           else
+             result.assign("global constructors keyed to ", 29);
+           // Output the disambiguation part as-is.
+           result += input + 11;
+         }
+         else
+           failure = true;
+       }
+       else if (input[1] == 'Z')
+       {
+         int cnt =
+             demangler_type::decode_encoding(result, input + 2, INT_MAX, id);
+         if (cnt < 0 || input[cnt + 2] != 0)
+           failure = true;
+       }
+       else
+         failure = true;
+      }
+
+      // Failure to demangle, return the mangled name.
+      if (failure)
+       result.assign(input, strlen(input));
+
+      return result;
+    }
+
+  // demangle::type()
+  // Demangle `input' which must be a zero terminated mangled type
+  // name as for instance returned by std::type_info::name().
+  template<typename Tp, typename Allocator>
+    typename demangle<Tp, Allocator>::string_type
+    demangle<Tp, Allocator>::type(char const* input,
+                              demangler::implementation_details const& id)
+    {
+      std::basic_string<char, std::char_traits<char>, Allocator> result;
+      if (input == NULL)
+       result = "(null)";
+      else
+      {
+       demangler::session<Tp, Allocator> demangler_session(input, INT_MAX, id);
+       if (!demangler_session.decode_type(result)
+           || demangler_session.remaining_input_characters())
+       {
+         // Failure to demangle, return the mangled name.
+         result = input;
+       }
+      }
+      return result;
+    }
+} // namespace __gnu_cxx
+
+#endif // __DEMANGLE_H
index 50727402c135d5f67e91491ac2dd091994ebd91f..5550686728ac633e8e48af2857033ef43b29ff6e 100644 (file)
@@ -1,6 +1,6 @@
 ## Makefile for the GNU C++ Support library.
 ##
-## Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+## Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
 ##
 ## Process this file with automake to produce Makefile.in.
 ##
@@ -34,6 +34,9 @@ noinst_LTLIBRARIES = libsupc++convenience.la
 headers = \
        exception new typeinfo cxxabi.h exception_defines.h
 
+c_sources = \
+       cp-demangle.c 
+
 sources = \
        del_op.cc \
        del_opnt.cc \
@@ -62,8 +65,8 @@ sources = \
        vec.cc \
        vterminate.cc
 
-libsupc___la_SOURCES = $(sources)
-libsupc__convenience_la_SOURCES = $(sources)
+libsupc___la_SOURCES = $(sources) $(c_sources)
+libsupc__convenience_la_SOURCES = $(sources) $(c_sources)
 
 glibcxxinstalldir = $(gxx_include_dir)
 glibcxxinstall_HEADERS = $(headers)
@@ -83,6 +86,29 @@ AM_CXXFLAGS = \
 AM_MAKEFLAGS = \
        "gxx_include_dir=$(gxx_include_dir)"
 
+
+# Use special rules for pulling things out of libiberty.  These
+# objects should be compiled with the "C" compiler, not the C++
+# compiler, and also should not use the C++ includes.
+C_INCLUDES = -I.. -I$(toplevel_srcdir)/libiberty -I$(toplevel_srcdir)/include
+C_COMPILE = \
+       $(CC) $(DEFS) $(C_INCLUDES) \
+       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+
+# LTCOMPILE is copied from LTCXXCOMPILE below.
+LTCOMPILE = $(LIBTOOL) --tag CC --tag disable-shared --mode=compile $(CC) \
+           $(DEFS) $(C_INCLUDES) $(LIBSUPCXX_PICFLAGS) \
+            $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+
+cp-demangle.c:
+       rm -f $@
+       $(LN_S) $(toplevel_srcdir)/libiberty/cp-demangle.c $@
+cp-demangle.lo: cp-demangle.c
+       $(LTCOMPILE) -DIN_GLIBCPP_V3 -Wno-error -c $<
+cp-demangle.o: cp-demangle.c
+       $(C_COMPILE) -DIN_GLIBCPP_V3 -Wno-error -c $<
+
+
 # libstdc++ libtool notes
 
 # 1) Need to explicitly set LTCXXCOMPILE so that AM_CXXFLAGS is
index f2ef55ee83b570179cf1b7f3cc0c675b53703aee..9945e8295ca3c3dc21469b48e046e2b39c1a7591 100644 (file)
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.7.9 from Makefile.am.
+# Makefile.in generated by automake 1.7.8 from Makefile.am.
 # @configure_input@
 
 # Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
@@ -42,7 +42,8 @@ ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
 AR = @AR@
 AS = @AS@
-ATOMICITY_INC_SRCDIR = @ATOMICITY_INC_SRCDIR@
+ATOMICITY_SRCDIR = @ATOMICITY_SRCDIR@
+ATOMIC_WORD_SRCDIR = @ATOMIC_WORD_SRCDIR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
 AUTOMAKE = @AUTOMAKE@
@@ -227,6 +228,10 @@ headers = \
        exception new typeinfo cxxabi.h exception_defines.h
 
 
+c_sources = \
+       cp-demangle.c 
+
+
 sources = \
        del_op.cc \
        del_opnt.cc \
@@ -256,8 +261,8 @@ sources = \
        vterminate.cc
 
 
-libsupc___la_SOURCES = $(sources)
-libsupc__convenience_la_SOURCES = $(sources)
+libsupc___la_SOURCES = $(sources) $(c_sources)
+libsupc__convenience_la_SOURCES = $(sources) $(c_sources)
 
 glibcxxinstalldir = $(gxx_include_dir)
 glibcxxinstall_HEADERS = $(headers)
@@ -279,6 +284,21 @@ AM_MAKEFLAGS = \
        "gxx_include_dir=$(gxx_include_dir)"
 
 
+# Use special rules for pulling things out of libiberty.  These
+# objects should be compiled with the "C" compiler, not the C++
+# compiler, and also should not use the C++ includes.
+C_INCLUDES = -I.. -I$(toplevel_srcdir)/libiberty -I$(toplevel_srcdir)/include
+C_COMPILE = \
+       $(CC) $(DEFS) $(C_INCLUDES) \
+       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+
+
+# LTCOMPILE is copied from LTCXXCOMPILE below.
+LTCOMPILE = $(LIBTOOL) --tag CC --tag disable-shared --mode=compile $(CC) \
+           $(DEFS) $(C_INCLUDES) $(LIBSUPCXX_PICFLAGS) \
+            $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+
+
 # libstdc++ libtool notes
 
 # 1) Need to explicitly set LTCXXCOMPILE so that AM_CXXFLAGS is
@@ -329,16 +349,22 @@ am__objects_1 = del_op.lo del_opnt.lo del_opv.lo del_opvnt.lo \
        eh_terminate.lo eh_throw.lo eh_type.lo eh_unex_handler.lo \
        guard.lo new_handler.lo new_op.lo new_opnt.lo new_opv.lo \
        new_opvnt.lo pure.lo tinfo.lo tinfo2.lo vec.lo vterminate.lo
-am_libsupc___la_OBJECTS = $(am__objects_1)
+am__objects_2 = cp-demangle.lo
+am_libsupc___la_OBJECTS = $(am__objects_1) $(am__objects_2)
 libsupc___la_OBJECTS = $(am_libsupc___la_OBJECTS)
 libsupc__convenience_la_LDFLAGS =
 libsupc__convenience_la_LIBADD =
-am_libsupc__convenience_la_OBJECTS = $(am__objects_1)
+am_libsupc__convenience_la_OBJECTS = $(am__objects_1) $(am__objects_2)
 libsupc__convenience_la_OBJECTS = $(am_libsupc__convenience_la_OBJECTS)
 
 DEFAULT_INCLUDES =  -I. -I$(srcdir) -I$(top_builddir)
 depcomp =
 am__depfiles_maybe =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(AM_LDFLAGS) $(LDFLAGS) -o $@
 CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
        $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
 CXXLD = $(CXX)
@@ -353,7 +379,7 @@ SOURCES = $(libsupc___la_SOURCES) $(libsupc__convenience_la_SOURCES)
 all: all-am
 
 .SUFFIXES:
-.SUFFIXES: .cc .lo .o .obj
+.SUFFIXES: .c .cc .lo .o .obj
 $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/fragment.am $(top_srcdir)/configure.ac $(ACLOCAL_M4)
        cd $(top_srcdir) && \
          $(AUTOMAKE) --foreign  libsupc++/Makefile
@@ -407,6 +433,15 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
+.c.o:
+       $(COMPILE) -c `test -f '$<' || echo '$(srcdir)/'`$<
+
+.c.obj:
+       $(COMPILE) -c `if test -f '$<'; then $(CYGPATH_W) '$<'; else $(CYGPATH_W) '$(srcdir)/$<'; fi`
+
+.c.lo:
+       $(LTCOMPILE) -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$<
+
 .cc.o:
        $(CXXCOMPILE) -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$<
 
@@ -530,7 +565,7 @@ install-am: all-am
 installcheck: installcheck-am
 install-strip:
        $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         INSTALL_STRIP_FLAG=-s \
          `test -z '$(STRIP)' || \
            echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
 mostlyclean-generic:
@@ -606,6 +641,14 @@ uninstall-am: uninstall-glibcxxinstallHEADERS uninstall-info-am \
        uninstall-info-am uninstall-toolexeclibLTLIBRARIES
 
 
+cp-demangle.c:
+       rm -f $@
+       $(LN_S) $(toplevel_srcdir)/libiberty/cp-demangle.c $@
+cp-demangle.lo: cp-demangle.c
+       $(LTCOMPILE) -DIN_GLIBCPP_V3 -Wno-error -c $<
+cp-demangle.o: cp-demangle.c
+       $(C_COMPILE) -DIN_GLIBCPP_V3 -Wno-error -c $<
+
 # We have to have rules modified from the default to counteract SUN make
 # prepending each of $(glibcxxinstall_HEADERS) with VPATH below.
 install-glibcxxinstallHEADERS: $(glibcxxinstall_HEADERS)
index 7b7f97767bebccec3f89b19a0719d105d9cc418a..d70d1af3ad130f7b429ae9e9819e307516114806 100644 (file)
@@ -101,7 +101,6 @@ sources = \
        complex_io.cc \
        ctype.cc \
        debug.cc \
-       demangle.cc \
        functexcept.cc \
        globals_locale.cc \
        globals_io.cc \
index 24e7796175e846d980bd45408a69eaac7d3fe1aa..99a6eb641244b5aaceee7dc3ca8a9833e14fe1ff 100644 (file)
@@ -220,12 +220,12 @@ AM_CPPFLAGS = $(GLIBCXX_INCLUDES)
 
 # Cross compiler support.
 toolexeclib_LTLIBRARIES = libstdc++.la
+@GLIBCXX_BUILD_VERSIONED_SHLIB_FALSE@version_arg = 
 
 # Symbol versioning for shared libraries.
 @GLIBCXX_BUILD_VERSIONED_SHLIB_TRUE@version_arg = -Wl,--version-script=libstdc++-symbol.ver
-@GLIBCXX_BUILD_VERSIONED_SHLIB_FALSE@version_arg = 
-@GLIBCXX_BUILD_VERSIONED_SHLIB_TRUE@version_dep = libstdc++-symbol.ver
 @GLIBCXX_BUILD_VERSIONED_SHLIB_FALSE@version_dep = 
+@GLIBCXX_BUILD_VERSIONED_SHLIB_TRUE@version_dep = libstdc++-symbol.ver
 
 # Source files linked in via configuration/make substitution for a
 # particular host.
@@ -256,7 +256,6 @@ sources = \
        complex_io.cc \
        ctype.cc \
        debug.cc \
-       demangle.cc \
        functexcept.cc \
        globals_locale.cc \
        globals_io.cc \
@@ -366,9 +365,9 @@ am__objects_1 = atomicity.lo codecvt_members.lo collate_members.lo \
        numeric_members.lo time_members.lo
 am__objects_2 = basic_file.lo c++locale.lo
 am__objects_3 = allocator.lo codecvt.lo complex_io.lo ctype.lo debug.lo \
-       demangle.lo functexcept.lo globals_locale.lo globals_io.lo \
-       ios.lo ios_failure.lo ios_init.lo ios_locale.lo limits.lo \
-       list.lo locale.lo locale_init.lo locale_facets.lo localename.lo \
+       functexcept.lo globals_locale.lo globals_io.lo ios.lo \
+       ios_failure.lo ios_init.lo ios_locale.lo limits.lo list.lo \
+       locale.lo locale_init.lo locale_facets.lo localename.lo \
        stdexcept.lo strstream.lo tree.lo allocator-inst.lo \
        concept-inst.lo fstream-inst.lo ext-inst.lo io-inst.lo \
        istream-inst.lo locale-inst.lo locale-misc-inst.lo misc-inst.lo \
diff --git a/libstdc++-v3/src/demangle.cc b/libstdc++-v3/src/demangle.cc
deleted file mode 100644 (file)
index 779076d..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-// C++ IA64 / g++ v3 demangler  -*- C++ -*-
-
-// Copyright (C) 2003, 2004 Free Software Foundation, Inc.
-// Written by Carlo Wood <carlo@alinoe.com>
-//
-// This file is part of the GNU ISO C++ Library.  This library is free
-// software; you can redistribute it and/or modify it under the
-// terms of the GNU General Public License as published by the
-// Free Software Foundation; either version 2, or (at your option)
-// any later version.
-
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License along
-// with this library; see the file COPYING.  If not, write to the Free
-// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-
-// As a special exception, you may use this file as part of a free software
-// library without restriction.  Specifically, if other files instantiate
-// templates or use macros or inline functions from this file, or you compile
-// this file and link it with other files to produce an executable, this
-// file does not by itself cause the resulting executable to be covered by
-// the GNU General Public License.  This exception does not however
-// invalidate any other reasons why the executable file might be covered by
-// the GNU General Public License.
-
-#include <bits/demangle.h>
-#include <cxxabi.h>
-
-// __cxa_demangle
-//
-// Demangle a C++ symbol or type name.
-//
-// `mangled-name' is a pointer to a null-terminated array of characters.
-// It may be either an external name, i.e. with a "_Z" prefix, or an
-// internal NTBS mangling, e.g. of a type for type_info.
-//
-// `buf' may be null.  If it is non-null, then n must also be non-null,
-// and buf is a pointer to an array, of at least *n characters, that
-// was allocated using malloc.
-//
-// `status' points to an int that is used as an error indicator. It is
-// permitted to be null, in which case the user just doesn't get any
-// detailed error information. 
-//
-// Returns: a pointer to a null-terminated array of characters, the
-//          demangled name.  Or NULL in case of failure.
-//
-// If there is an error in demangling, the return value is a null pointer.
-// The user can examine *status to find out what kind of error occurred.
-// Meaning of error indications:
-//
-//     *  0: success
-//     * -1: memory allocation failure
-//     * -2: invalid mangled name
-//     * -3: invalid arguments (e.g. buf nonnull and n null) 
-//
-
-namespace __cxxabiv1 
-{
-  namespace 
-  {
-    char* const error = 0;
-
-    enum status_codes 
-    {
-      success = 0,
-      memory_allocation_failure = -1,
-      invalid_mangled_name = -2,
-      invalid_argument = -3
-    };
-
-    inline char*
-    failure(status_codes error_code, int* status)
-    {
-      if (status)
-       *status = error_code;
-      return error;
-    }
-
-    char*
-    finish(char const* demangled_name, size_t demangled_name_size,
-          char* buf, size_t* n, int* status)
-    {
-      if (!buf || *n < demangled_name_size + 1)
-      {
-       if (n)
-         *n = demangled_name_size + 1;
-       buf = (char*)realloc(buf, demangled_name_size + 1);
-       if (!buf)
-         return failure(memory_allocation_failure, status);
-      }
-      if (status)
-       *status = success;
-      std::strncpy(buf, demangled_name, demangled_name_size);
-      buf[demangled_name_size] = 0;
-      return buf;
-    }
-  } // namespace
-
-  char*
-  __cxa_demangle(char const* mangled_name, char* buf, std::size_t* n, 
-                int* status)
-  {
-    try {
-      using namespace __gnu_cxx;
-      typedef demangler::session<std::allocator<char> > session_type;
-
-      if (!mangled_name || (buf && !n))
-       return failure(invalid_argument, status);
-
-      std::string result;
-      if (mangled_name[0] == '_')              
-      {
-       // External name?
-       if (mangled_name[1] == 'Z')             
-       {
-         // C++ name?
-         int cnt = session_type::
-             decode_encoding(result, mangled_name + 2, INT_MAX);
-         if (cnt < 0 || mangled_name[cnt + 2] != 0)
-           return failure(invalid_mangled_name, status);
-         return finish(result.data(), result.size(), buf, n, status);
-       }
-       else if (mangled_name[1] == 'G')        
-       {
-         // Possible _GLOBAL__ extension?
-         if (!std::strncmp(mangled_name, "_GLOBAL__", 9) 
-             && (mangled_name[9] == 'D' || mangled_name[9] == 'I')
-             && mangled_name[10] == '_')
-         {
-           if (mangled_name[9] == 'D')
-             result.assign("global destructors keyed to ", 28);
-           else
-             result.assign("global constructors keyed to ", 29);
-           // Output the disambiguation part as-is.
-           result += mangled_name + 11;
-           return finish(result.data(), result.size(), buf, n, status);
-         }
-       }
-      }
-
-      // Ambiguities are possible between extern "C" object names and
-      // internal built-in type names, e.g. "i" may be either an object
-      // named "i" or the built-in "int" type.  Such ambiguities should
-      // be resolved to user names over built-in names.  Builtin types
-      // are any single lower case character.  Any other single
-      // character is not a mangled type so we can treat those the same
-      // here.
-      if (mangled_name[1] == 0)
-       return finish(mangled_name, 1, buf, n, status);
-
-      // Not a built-in type or external name, try to demangle input as
-      // NTBS mangled type name.
-      session_type demangler_session(mangled_name, INT_MAX);
-      if (!demangler_session.decode_type(result) 
-         || demangler_session.remaining_input_characters())
-      {
-       // Failure to demangle, assume extern "C" name.
-       result = mangled_name;          
-      }
-      return finish(result.data(), result.size(), buf, n, status);
-    } catch (std::bad_alloc&) {
-      return failure(memory_allocation_failure, status);
-    }
-  }
-} // namespace __cxxabiv1
-
-// Explicit instantiations.
-namespace __gnu_cxx
-{
-  template class demangler::qualifier_list<std::allocator<char> >;
-  template class demangler::session<std::allocator<char> >;
-} // namespace __gnu_cxx
-
-namespace std
-{
-  template
-    void vector<int>::_M_insert_aux(vector<int>::iterator, const int&);
-
-  typedef __gnu_cxx::demangler::substitution_st value_type1;
-  template
-    void vector<value_type1>::_M_insert_aux(vector<value_type1>::iterator, 
-                                           const value_type1&);
-
-  typedef __gnu_cxx::demangler::qualifier<std::allocator<char> > value_type2;
-  template
-    void vector<value_type2>::_M_insert_aux(vector<value_type2>::iterator, 
-                                           const value_type2&);
-}
-
index 585cfe0435c06e8744faa79220a802d1be8e2b5b..e7c41e7b966f7fa3fd7ca3842dc64bc9ff748914 100644 (file)
@@ -31,7 +31,7 @@ int main()
   // extern "C" function 
   // extern "C" float f(void) { };
   // T f
-  verify_demangle("f", "f");
+  verify_demangle("f", "error code = -2: invalid mangled name");
 
   return 0;
 }
index ce33e53894aabcb8cf63841a742b0082fbd66c3a..bbae9381e7d8a7284eac113cd4661cd1aa54762c 100644 (file)
@@ -31,7 +31,7 @@ int main()
   // or variable "f" 
   // int f;
   // B f
-  verify_demangle("f", "f");  
+  verify_demangle("f", "error code = -2: invalid mangled name");
 
   return 0;
 }
index e5405b2f6e9c09df5c59a7cfcd597ed7a02646eb..6fef1314d993e03de2b2b6418cdd6053ccbb4c0a 100644 (file)
@@ -28,7 +28,8 @@ int main()
   using namespace __gnu_test;
 
   // cplus-dem CORE
-  verify_demangle("_X11TransParseAddress", "_X11TransParseAddress");
+  verify_demangle("_X11TransParseAddress",
+                 "error code = -2: invalid mangled name");
 
   return 0;
 }
index 8dbfcb1f56e073af7083ba82843d813107f90ce4..78ae363a62b611b42502da4260c11bc3d6f10bdc 100644 (file)
@@ -29,14 +29,14 @@ int main()
 
 // 2003/11/07, libstdc++/12736
 verify_demangle("_Z3fooIA6_KiEvA9_KT_rVPrS4_",
-               "void foo<int const [6]>(int const [9][6], int const restrict (* volatile restrict) [9][6])");
+               "void foo<int const [6]>(int const [9][6], int restrict const (* volatile restrict) [9][6])");
 // 2003/11/12, libstdc++/12947
 verify_demangle("_Z1fILi5E1AEvN1CIXqugtT_Li0ELi1ELi2EEE1qE",
                 "void f<5, A>(C<(((5) > (0))) ? (1) : (2)>::q)");
 verify_demangle("_Z1fILi5EEvN1AIXcvimlT_Li22EEE1qE",
                 "void f<5>(A<(int)((5) * (22))>::q)");
 verify_demangle("_Z1fPFYPFiiEiE",
-                "f(int (*(*) [extern \"C\"] (int))(int))");
+                "f(int (*(*)(int))(int))");
 verify_demangle("_Z1fI1XENT_1tES2_",
                 "X::t f<X>(X::t)");
 verify_demangle("_Z1fILi5E1AEvN1CIXstN1T1tEEXszsrS2_1tEE1qE",