demangle.h   [plain text]


// 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