// $Header: /cvsroot/libcw/src/libcwd/demangle3.cc,v 1.10 2001/09/23 04:13:50 libcw Exp $
//
// Copyright (C) 2001, by
//
// Carlo Wood, Run on IRC <carlo@alinoe.com>
// RSA-1024 0x624ACAD5 1997-01-26                    Sign & Encrypt
// Fingerprint16 = 32 EC A7 B6 AC DB 65 A6  F6 F6 55 DD 1C DC FF 61
//
// This file may be distributed under the terms of the Q Public License
// version 1.0 as appearing in the file LICENSE.QPL included in the
// packaging of this file.
//
//
// void demangle_type(char const* in, std::string& out);
//
// where, `in' is a mangled type name as returned by typeid(OBJECT).name(),
// `in' does not have to be null terminated.  When `in' is NULL then the string "(null)" is returned.
//
// void demangle_symbol(char const* in, std::string& out);
//
// where, `in' is a mangled symbol name as returned by asymbol::name (where asymbol is defined by libbfd),
// which is the same as `location_st::func'.  Note that `in' must be null terminated.  When `in' is NULL
// then the string "(null)" is returned.
//
// Currently this file has been tested with gcc-3.0.
//
// The description of how the mangling is done in the new ABI was found on
// http://reality.sgi.com/dehnert_engr/cxx/abi.html#mangling-type:
//

#undef CPPFILTCOMPATIBLE

//#include "sys.h"

#ifdef __GNUC__

#if __GXX_ABI_VERSION > 0

#include <limits>
#include <vector>
#include <string>
#include <ctype.h>
#include "demangle.h"

#undef CWASSERT
#undef NEW
#define CWASSERT(x)
#define NEW(x) new x
#define RETURN return M_result
#define FAILURE do { M_result = false; return false; } while(0)

namespace libcw {
  namespace debug {

namespace {

  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) { }
  };

  class demangler_ct {
    friend class qualifiers_ct;
  private:
    char const* M_str;
    int M_pos;
    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;
    bool M_decoding_pointer_to_member_class_type;
    std::string M_function_name;
    std::vector<int> M_template_arg_pos;
    int M_template_arg_pos_offset;
    std::vector<substitution_st> M_substitutions_pos;

  public:
    explicit demangler_ct(char const* in) :
        M_str(in), M_pos(0), 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_decoding_pointer_to_member_class_type(false), M_template_arg_pos_offset(0)
        { }

  private:
    inline char current(void) const { return M_str[M_pos]; }
    inline char next(void) { return M_str[++M_pos]; }
    inline char eat_current(void) { return M_str[M_pos++]; }
    struct demangler_ct& skip(int count) { M_pos += count; return *this; }
    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 = 0)
    {
      if (!M_inside_substitution)
      {
        M_substitutions_pos.push_back(substitution_st(start_pos, sub_type, number_of_prefixes));
      }
    }

  private:
    bool decode_bare_function_type(std::string& output);
    bool decode_builtin_type(std::string& output);
    bool decode_call_offset(std::string& output);
    bool decode_class_enum_type(std::string& output);
    bool decode_expression(std::string& output);
    bool decode_literal(std::string& output);
    bool decode_local_name(std::string& output);
    bool decode_name(std::string& output, std::string& nested_name_qualifiers);
    bool decode_nested_name(std::string& output, std::string& qualifiers);
    bool decode_number(std::string& output);
    bool decode_operator_name(std::string& output);
    bool decode_source_name(std::string& output);
    bool decode_substitution(std::string& output, qualifiers_ct* qualifiers = NULL);
    bool decode_template_args(std::string& output);
    bool decode_template_param(std::string& output, qualifiers_ct* qualifiers = NULL);
    bool decode_unqualified_name(std::string& output);
    bool decode_unscoped_name(std::string& output);
    inline bool decode_decimal_integer(std::string& output);
    inline bool decode_special_name(std::string& output);

  public:
    static int decode_encoding(char const*, std::string&);
    bool decode_type(std::string& output, qualifiers_ct* qualifiers = NULL);
  };

  enum simple_qualifier_nt {
    complex_or_imaginary = 'G',
    pointer = 'P',
    reference = 'R'
  };

  enum cv_qualifier_nt {
    cv_qualifier = 'K'
  };

  enum param_qualifier_nt {
    vendor_extention = 'U',
    array = 'A',
    pointer_to_member = 'M'
  };

  class qualifier_ct {
  private:
    char M_qualifier1;
    char M_qualifier2;
    char M_qualifier3;
    mutable unsigned char cnt;
    std::string M_optional_type;
    int M_start_pos;
    bool M_part_of_substitution;
  public:
    qualifier_ct(int start_pos, simple_qualifier_nt qualifier, int inside_substitution) :
        M_qualifier1(qualifier),
        M_start_pos(start_pos),
        M_part_of_substitution(inside_substitution) { }
    qualifier_ct(int start_pos, cv_qualifier_nt qualifier, 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_ct(int start_pos, param_qualifier_nt qualifier, std::string optional_type, int inside_substitution) :
        M_qualifier1(qualifier),
        M_optional_type(optional_type),
        M_start_pos(start_pos),
        M_part_of_substitution(inside_substitution) { }
    int start_pos(void) const { return M_start_pos; }
    char first_qualifier(void) const { cnt = 1; return M_qualifier1; }
    char next_qualifier(void) const { return (++cnt == 2) ? M_qualifier2 : (cnt == 3) ? M_qualifier3 : 0; }
    std::string const& optional_type(void) const { return M_optional_type; }
    bool part_of_substitution(void) const { return M_part_of_substitution; }
  };

  class qualifiers_ct {
  private:
    bool M_printing_suppressed;
    std::vector<qualifier_ct> M_qualifier_starts;
    demangler_ct& M_demangler;
  public:
    qualifiers_ct(demangler_ct& demangler) : M_printing_suppressed(false), M_demangler(demangler) { }
    void add_qualifier_start(simple_qualifier_nt qualifier, int start_pos, int inside_substitution)
        { M_qualifier_starts.push_back(qualifier_ct(start_pos, qualifier, inside_substitution)); }
    void add_qualifier_start(cv_qualifier_nt qualifier, int start_pos, int count, int inside_substitution)
        { M_qualifier_starts.push_back(qualifier_ct(start_pos, qualifier, &M_demangler.M_str[start_pos], count, inside_substitution)); }
    void add_qualifier_start(param_qualifier_nt qualifier, int start_pos, std::string optional_type, int inside_substitution)
        { M_qualifier_starts.push_back(qualifier_ct(start_pos, qualifier, optional_type, inside_substitution)); }
    void decode_qualifiers(std::string& output);
    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(); }
  };

  //
  // <decimal-integer> ::= 0
  //                   ::= 1|2|3|4|5|6|7|8|9 [<digit>+]
  // <digit>           ::= 0|1|2|3|4|5|6|7|8|9
  //
  // {anonymous}::
  inline bool demangler_ct::decode_decimal_integer(std::string& 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] <decimal-integer>
  //
  // {anonymous}::
  bool demangler_ct::decode_number(std::string& output)
  {
    if (current() != 'n')
      decode_decimal_integer(output);
    else
    {
      output += '-';
      eat_current();
      decode_decimal_integer(output);
    }
    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* builtin_type[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
  };

  //
  // {anonymous}::
  bool demangler_ct::decode_builtin_type(std::string& output)
  {
    char const* bt;
    if (!islower(current()) || !(bt = builtin_type[current() - 'a']))
      FAILURE;
    output += bt;
    eat_current();
    RETURN;
  }

  // <class-enum-type> ::= <name>
  //
  // {anonymous}::
  bool demangler_ct::decode_class_enum_type(std::string& output)
  {
    std::string nested_name_qualifiers;
    if (!decode_name(output, nested_name_qualifiers))
      FAILURE;
    output += nested_name_qualifiers;
    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
  //
  // {anonymous}::
  bool demangler_ct::decode_substitution(std::string& output, qualifiers_ct* qualifiers = NULL)
  {
    CWASSERT( current() == 'S');
    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();
          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();
          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();
          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();
          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();
          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();
          RETURN;
        case 't':
          output += "std";
          eat_current();
          if (qualifiers)
            qualifiers->printing_suppressed();
          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
              FAILURE;
          }
          ++value;
          break;
      }
    }
    eat_current();
    if (value >= M_substitutions_pos.size() ||
        M_inside_type > 20)                             // Rather than core dump.
      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))
              FAILURE;
          }
          else
          {
            if (cnt < substitution.M_number_of_prefixes)
              output += "::";
            if (current() == 'S')
            {
              if (!decode_substitution(output))
                FAILURE;
            }
            else if (!decode_unqualified_name(output))
              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;
    RETURN;
  }

  // <template-param> ::= T_                            # first template parameter
  //                  ::= T <parameter-2 non-negative number> _
  //
  // {anonymous}::
  bool demangler_ct::decode_template_param(std::string& output, qualifiers_ct* qualifiers = NULL)
  {
    if (current() != 'T')
      FAILURE;
    unsigned int value = 0;
    char c;
    if ((c = next()) != '_')
    {
      while(isdigit(c))
      {
        value = value * 10 + c - '0';
        c = next();
      }
      ++value;
    }
    if (eat_current() != '_')
      FAILURE;
    value += M_template_arg_pos_offset;
    if (value >= M_template_arg_pos.size())
      FAILURE;
    int saved_pos = M_pos;
    M_pos = M_template_arg_pos[value];
    if (M_inside_type > 20)             // Rather than core dump.
      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;
    RETURN;
  }

  bool demangler_ct::decode_literal(std::string& output)
  {
    eat_current();      // Eat the 'L'.
    if (current() == '_')
    {
      if (next() != 'Z')
        FAILURE;
      eat_current();
      if ((M_pos += decode_encoding(M_str + M_pos, output)) < 0)
        FAILURE;
    }
    else
    {
      // Special cases
      if (current() == 'b')
      {
        if (next() == '0')
          output += "false";
        else
          output += "true";
        eat_current();
        RETURN;
      }
      char c = current();
      if (c == 'i' || c == 'j' || c == 'l' || c == 'm' || c == 'x' || c == 'y')
        eat_current();
      else
      {
        output += '(';
        if (!decode_type(output))
          FAILURE;
        output += ')';
      }
      if (!decode_number(output))
        FAILURE;
      if (c == 'j' || c == 'm' || c == 'y')
        output += 'u';
      if (c == 'l' || c == 'm')
        output += 'l';
      if (c == 'x' || c == 'y')
        output += "ll";
    }
    RETURN;
  }

  // <operator-name> ::= nw                             # new           
  //                 ::= na                             # new[]
  //                 ::= dl                             # delete        
  //                 ::= da                             # delete[]      
  //                 ::= 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                             # ?             
  //                 ::= sz                             # sizeof        
  //                 ::= sr                             # scope resolution (::), see below        
  //                 ::= 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:
  //                                              horizontal
  //    .....................................     offset + 'a'
  // a, ||a||d|||||||||n||||s||||||||||||||||||     2
  // c, || || ||lm|o||| |||| ||||||||||||||||||    -3
  // d, || a| |e  | ||l |||| |||v||||||||||||||     3
  // e, ||  | |   o q|  |||| ||| ||||||||||||||    -4
  // g, |e  | |      |  t||| ||| ||||||||||||||    -3
  // i, |   | |      |   ||| ||| ||||||||||x|||    12
  // l, |   | |      e   ||| ||| ||st|||||| |||     9
  // m, |   | |          ||| ||| |i  lm|||| |||    18
  // n, a   e g          ||t |w| |     |||| |||     0
  // o,                  ||  | | |     ||o| r||    19
  // p,                  lm  p | t     || |  ||     6
  // q,                        |       || u  ||    14
  // r,                        |       |m    |s    20
  // s,                        r       z     |      6
  //    .....................................
  // ^            ^__ second character
  // |___ first character
  //

  // Putting that solution in tables:

  // {anonymous}::
  char offset_table[1 + CHAR_MAX - CHAR_MIN /* ascii value of first character */ ] = {
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   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    l    m    n    o    p    q    r    s    t    u    v
    0, -95,   0,-100, -94,-101,   0,-100,   0, -85,   0,   0, -88, -79, -97, -78, -91, -83, -77, -91,   0,   0,   0,
#else
    0, 161,   0, 156, 162, 155,   0, 156,   0, 171,   0,   0, 168, 177, 159, 178, 165, 173, 179, 165,   0,   0,   0,
#endif
    // ... more zeros
  };

  // {anonymous}::
  struct entry_st { char const* opcode; char const* symbol_name; bool unary; } symbol_name_table[39] = {
    { "na",  "operator new[]", true },
    { "ge",  "operator>=", false },
    { "aa",  "operator&&", false },
    { "da",  "operator delete[]", true },
    { "ne",  "operator!=", false },
    { "ad",  "operator&", true },       // unary
    { "ng",  "operator-", true },       // unary
    { "de",  "operator*", true },       // unary
    { "cl",  "operator()", true },
    { "cm",  "operator,", false },
    { "eo=", "operator^", false },
    { "co",  "operator~", false },
    { "eq",  "operator==", false },
    { "le",  "operator<=", false },
    { "dl",  "operator delete", true },
    { "an=", "operator&", false },
    { "gt",  "operator>", false },
    { "pl=", "operator+", false },
    { "pm",  "operator->*", false },
    { "nt",  "operator!", true },
    { "as=", "operator", false },
    { "pp",  "operator++", true },
    { "nw",  "operator new", true },
    { "sr",  "::", true },
    { "dv=", "operator/", false },
    { "pt",  "operator->", false },
    { "mi=", "operator-", false },
    { "ls=", "operator<<", false },
    { "lt",  "operator<", false },
    { "ml=", "operator*", false },
    { "mm",  "operator--", true },
    { "sz",  "sizeof", true },
    { "rm=", "operator%", false },
    { "oo",  "operator||", false },
    { "qu",  "operator?", false },
    { "ix",  "operator[]", true },
    { "or=", "operator|", false },
    { "", NULL, false },
    { "rs=", "operator>>", false }
  };

  // {anonymous}::
  bool demangler_ct::decode_operator_name(std::string& output)
  {

    char opcode0 = current();
    char opcode1 = tolower(next());

    register char hash;
    if ((hash = offset_table[opcode0 - CHAR_MIN]))
    {
      hash += opcode1;
      if (
#if (CHAR_MIN<0)
          hash >= 0 &&
#endif
          hash < 39)
      {
        entry_st entry = symbol_name_table[hash];
        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 == 27 || hash == 28)
            M_template_args_need_space = true;
          RETURN;
        }
        else if (opcode0 == 'c' && opcode1 == 'v')
        {
          eat_current();
          output += "operator ";
          if (!decode_type(output))
            FAILURE;
          if (!M_inside_template_args)
            M_name_is_conversion_operator = true;
          RETURN;
        }
      }
    }
    FAILURE;
  }

  //
  // <expression> ::= <unary operator-name> <expression>
  //              ::= <binary operator-name> <expression> <expression>
  //              ::= <expr-primary>
  //
  // <expr-primary> ::= <template-param>                        # Starts with a T
  //                ::= L <type> <value number> E               # literal
  //                ::= L <mangled-name> E                      # external name
  //
  // {anonymous}::
  bool demangler_ct::decode_expression(std::string& output)
  {
    if (current() == 'T')
    {
      if (!decode_template_param(output))
        FAILURE;
      RETURN;
    }
    else if (current() == 'L')
    {
      if (!decode_literal(output))
        FAILURE;
      if (current() != 'E')
        FAILURE;
      eat_current();
      RETURN;
    }
    else
    {
      char opcode0 = current();
      char opcode1 = tolower(next());

      register char hash;
      if ((hash = offset_table[opcode0 - CHAR_MIN]))
      {
        hash += opcode1;
        if (
#if (CHAR_MIN<0)
            hash >= 0 &&
#endif
            hash < 39)
        {
          entry_st entry = symbol_name_table[hash];
          if (entry.opcode[0] == opcode0 && entry.opcode[1] == opcode1 && (opcode1 == current() || entry.opcode[2] == '='))
          {
            char const* p = entry.symbol_name;
            if (!strncmp("operator", p, 8))
              p += 8;
            if (*p == ' ')
              ++p;
            if (entry.unary)
              output += p;
            bool is_eq = (opcode1 != current());
            eat_current();
            output += '(';
            if (!decode_expression(output))
              FAILURE;
            output += ')';
            if (!entry.unary)
            {
              output += ' ';
              output += p;
              if (is_eq)
                output += '=';
              output += ' ';
              output += '(';
              if (!decode_expression(output))
                FAILURE;
              output += ')';
            }
            RETURN;
          }
        }
      }
    }
    FAILURE;
  }

  //
  // <template-args> ::= I <template-arg>+ E
  // <template-arg> ::= <type>                                  # type or template
  //                ::= L <type> <value number> E               # literal
  //                ::= L_Z <encoding> E                        # external name
  //                ::= X <expression> E                        # expression
  // {anonymous}::
  bool demangler_ct::decode_template_args(std::string& output)
  {
    if (eat_current() != 'I')
      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))
          FAILURE;
        if (current() != 'E')
          FAILURE;
        eat_current();
      }
      else if (current() == 'L')
      {
        if (!decode_literal(output))
          FAILURE;
        if (current() != 'E')
          FAILURE;
        eat_current();
      }
      else if (!decode_type(output))
        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;
    }
    RETURN;
  }

  // <bare-function-type> ::= <signature type>+         # types are parameter types
  //
  // {anonymous}::
  bool demangler_ct::decode_bare_function_type(std::string& output)
  {
    if (M_saw_destructor)
    {
      if (eat_current() != 'v' || (current() != 'E' && current() != 0))
        FAILURE;
      output += "()";
      M_saw_destructor = false;
      RETURN;
    }
    output += '(';
    M_template_args_need_space = false;
    if (!decode_type(output))                   // Must have at least one parameter
      FAILURE;
    while (current() != 'E' && current() != 0)
    {
      output += ", ";
      if (!decode_type(output))
        FAILURE;
    }
    output += ')';
    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 pair (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<Q2><C>F<R><B>E        ==> R (C::*Q)B Q2               "<C>", "<Q2><C>", "F<R><B>E" (<R> and <B> recursive), "M<Q2><C>F<R><B>E".
  // <Q>F<R><B>E                ==> R (Q)B                      "<R>", "<B>" (<B> recursive) and "F<R><B>E".
  // <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>..."
  // A<I>               ==>  [I]                                "A<I>..." (<I> recursive).
  // <Q>A<I>            ==>  (Q) [I]                            "A<I>..." (<I> recursive).
  // <Q>M<C>            ==> C::*Q                               "M<C>..." (<C> recursive).
  //  
  // 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 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 :/
  //

  void qualifiers_ct::decode_qualifiers(std::string& output)
  {
    std::string postfix;
    for(std::vector<qualifier_ct>::reverse_iterator iter = M_qualifier_starts.rbegin(); iter != M_qualifier_starts.rend();)
    {
      if (!(*iter).part_of_substitution())
      {
        int saved_inside_substitution = M_demangler.M_inside_substitution;
        M_demangler.M_inside_substitution = 0;
        M_demangler.add_substitution((*iter).start_pos(), type);
        M_demangler.M_inside_substitution = saved_inside_substitution;
      }
      char qualifier = (*iter).first_qualifier();
      for(; qualifier; qualifier = (*iter).next_qualifier())
      {
        switch(qualifier)
        {
          case 'P':
            output += "*";
            break;
          case 'R':
            output += "&";
            break;
          case 'K':
            output += " const";
            continue;
          case 'V':
            output += " volatile";
            continue;
          case 'r':
            output += " restrict";
            continue;
          case 'A':
          {
            std::string index = (*iter).optional_type();
            if (++iter != M_qualifier_starts.rend())
            {
              output += " (";
              postfix = ") [" + index + "]" + postfix;
            }
            else
            {
              output += " [";
              output += index;
              output += "]";
            }
            break;
          }
          case 'M':
            output += (*iter).optional_type();
            output += "::*";
            break;
          case 'U':
            output += " ";
            output += (*iter).optional_type();
            break;
          case 'G':     // Only here so we added a substitution.
            break;
        }
        break;
      }
      if (qualifier != 'A')
        ++iter;
    }
    output += postfix;
    M_printing_suppressed = false;
  }

  //
  // {anonymous}::
  bool demangler_ct::decode_type(std::string& output, qualifiers_ct* qualifiers = NULL)
  {
    ++M_inside_type;
    bool recursive_template_param_or_substitution_call;
    if (!(recursive_template_param_or_substitution_call = qualifiers))
      qualifiers = NEW( qualifiers_ct(*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();
          std::string source_name;
          if (!decode_source_name(source_name))
          {
            failure = true;
            break;
          }
          qualifiers->add_qualifier_start(vendor_extention, start_pos, source_name, M_inside_substitution);
          continue;
        }
        case 'A':
        {
          // <array-type> ::= A <positive dimension number> _ <element type>
          //              ::= A [<dimension expression>] _ <element type>
          //
          std::string 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':
        {
          eat_current();
          qualifiers_ct* class_type_qualifiers = NEW( qualifiers_ct(*this) );
          M_decoding_pointer_to_member_class_type = true;
          std::string class_type;
          if (!decode_type(class_type, class_type_qualifiers) || !class_type_qualifiers->suppressed())
          {
            delete class_type_qualifiers;
            failure = true;
            break;
          }
          M_decoding_pointer_to_member_class_type = false;
          if (M_str[start_pos + 1] != 'S' || M_str[start_pos + 2] == 't')
            add_substitution(start_pos + 1, type);                                      // substitution: "<C>".
          if (class_type_qualifiers->size() > 0)        // Must be CV-qualifiers and a member function pointer.
          {
            // <Q>M<Q2><C>F<R><B>E      ==> R (C::*Q)B Q2               "<C>", "<Q2><C>", "F<R><B>E" (<R> and <B> recursive), "M<Q2><C>F<R><B>E".
            std::string member_function_qualifiers;
            class_type_qualifiers->decode_qualifiers(member_function_qualifiers);       // substitution(s): "<Q2><C>".
            delete class_type_qualifiers;
            if (eat_current() != 'F')
            {
              failure = true;
              break;
            }
            // Return type.  FIXME: constructors, destructors and conversion operators don't have a return type.
            if (!decode_type(output))                                           // substitution: "F<R><B>E" (<R> and <B> recursive).
            {
              failure = true;
              break;
            }
            output += " (";
            output += class_type;
            output += "::*";
            std::string bare_function_type;
            if (!decode_bare_function_type(bare_function_type) || eat_current() != 'E')
            {
              failure = true;
              break;
            }
            add_substitution(start_pos, type);                                  // substitution: "M<Q2><C>F<R><B>E".
            qualifiers->decode_qualifiers(output);                              // substitution: all qualified types if any.
            output += ")";
            output += bare_function_type;
            output += member_function_qualifiers;
            goto decode_type_exit;
          }
          delete class_type_qualifiers;
          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               "<T>", "G<T>" (<T> recursive).
      // <Q>C<T>                ==> complex T Q                 "<T>", "C<T>" (<T> recursive).
      if (current() == 'C' || current() == 'G')
      {
        output += 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':
        {
          // <Q>F<R><B>E                ==> R (Q)B                      "<R>", "<B>" (<B> recursive) and "F<R><B>E".
          eat_current();
          // Return type.  FIXME: constructors, destructors and conversion operators don't have a return type.
          if (!decode_type(output))                             // substitution: "<R>".
          {
            failure = true;
            break;
          }
          output += " (";
          std::string bare_function_type;
          if (!decode_bare_function_type(bare_function_type) || eat_current() != 'E')   // substitution: "<B>" (<B> recursive).
          {
            failure = true;
            break;
          }
          add_substitution(start_pos, type);                    // substitution: "F<R><B>E"
          qualifiers->decode_qualifiers(output);                // substitution: all qualified types, if any.
          output += ")";
          output += bare_function_type;
          break;
        }
        case 'T':
          if (!decode_template_param(output, qualifiers))
          {
            failure = true;
            break;
          }
          if (current() == 'I')
          {
            add_substitution(start_pos, template_template_param);       // substitution: "<template-template-param>".
            if (!decode_template_args(output))
            {
              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>".
            if (!M_decoding_pointer_to_member_class_type)
              qualifiers->decode_qualifiers(output);            // substitution: all qualified types, if any.
          }
          break;
        case 'S':
          if (M_str[M_pos + 1] != 't')
          {
            if (!decode_substitution(output, qualifiers))
            {
              failure = true;
              break;
            }
            if (current() == 'I')
            {
              if (!decode_template_args(output))
              {
                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() && !M_decoding_pointer_to_member_class_type)
              qualifiers->decode_qualifiers(output);                    // 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                         "<T>" (<T> recursive).
          if (!decode_class_enum_type(output))
          {
            failure = true;
            break;
          }
          if (!recursive_template_param_or_substitution_call)
          {
            add_substitution(start_pos, type);                          // substitution: "<class-enum-type>".
            qualifiers->decode_qualifiers(output);                      // substitution: all qualified types, if any.
          }
          else
            qualifiers->printing_suppressed();
          break;
        default:
          // <Q><T>                     ==> T Q                         "<T>" (<T> recursive).
          if (!decode_builtin_type(output))
          {
            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(output);
          else
            qualifiers->printing_suppressed();
          break;
      }
    }
decode_type_exit:
    --M_inside_type;
    if (!recursive_template_param_or_substitution_call)
      delete qualifiers;
    if (failure)
      FAILURE;
    RETURN;
  }

  // <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>
  //          ::= # empty
  //          ::= <substitution>
  //
  // <template-prefix> ::= <prefix> <template unqualified-name>
  //                   ::= <substitution>
  //
  // {anonymous}::
  bool demangler_ct::decode_nested_name(std::string& output, std::string& qualifiers)
  {

    if (current() != 'N')
      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))
          FAILURE;
      }
      else if (current() == 'I')
      {
        if (!decode_template_args(output))
          FAILURE;
        if (current() != 'E')
          add_substitution(substitution_start, nested_name_prefix, number_of_prefixes);
                                                                        // substitution: "<template-prefix> <template-args>".
      }
      else
      {
        if (!decode_unqualified_name(output))
          FAILURE;
        if (current() != 'E')
          add_substitution(substitution_start, (current() == 'I') ? nested_name_template_prefix : nested_name_prefix, number_of_prefixes);
                                                                        // substitution: "<prefix> <unqualified-name>" or
                                                                        // "<prefix> <template unqualified-name>".
      }
      if (current() == 'E')
      {
        eat_current();
        RETURN;
      }
      if (current() != 'I')
        output += "::";
      else if (M_template_args_need_space)
        output += ' ';
      M_template_args_need_space = false;
    }
    FAILURE;
  }

  // <local-name> := Z <function encoding> E <entity name> [<discriminator>]
  //              := Z <function encoding> E s [<discriminator>]
  // <discriminator> := _ <non-negative number>
  //
  // {anonymous}::
  bool demangler_ct::decode_local_name(std::string& output)
  {
    if (current() != 'Z')
      FAILURE;
    if ((M_pos += decode_encoding(M_str + M_pos + 1, output) + 1) < 0 || eat_current() != 'E')
      FAILURE;
    output += "::";
    if (current() == 's')
    {
      eat_current();
      output += "string literal";
    }
    else
    {
      std::string nested_name_qualifiers;
      if (!decode_name(output, nested_name_qualifiers))
        FAILURE;
      output += nested_name_qualifiers;
    }
    std::string discriminator;
    if (current() == '_' && next() != 'n' && !decode_number(discriminator))
      FAILURE;
    RETURN;
  }

  // <source-name> ::= <positive length number> <identifier>
  //
  // {anonymous}::
  bool demangler_ct::decode_source_name(std::string& output)
  {
    int length = current() - '0';
    if (length < 1 || length > 9)
      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)";
      skip(length);
    }
    else
      while(length--)
      {
        if (current() == 0)
          FAILURE;
        output += eat_current();
      }
    RETURN;
  }

  // <unqualified-name> ::= <operator-name>                             # Starts with lower case
  //                    ::= <ctor-dtor-name>                            # Starts with 'C' or 'D'
  //                    ::= <source-name>                               # Starts with a digit
  //
  // {anonymous}::
  bool demangler_ct::decode_unqualified_name(std::string& output)
  {
    if (isdigit(current()))
    {
      if (!M_inside_template_args)
      {
        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))
          FAILURE;
        output += M_function_name;
      }
      else if (!decode_source_name(output))
        FAILURE;
      RETURN;
    }
    if (islower(current()))
    {
      if (!M_inside_template_args)
      {
        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))
          FAILURE;
        output += M_function_name;
      }
      RETURN;
    }
    if (current() == 'C' || current() == 'D')
    {
      if (M_inside_template_args)
        FAILURE;
      // <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
      //
      // {anonymous}::
      if (current() == 'C')
      {
        char c = next();
        if (c < '1' || c > '3')
          FAILURE;
      }
      else
      {
        char c = next();
        if (c < '0' || c > '2')
          FAILURE;
        output += '~';
        M_saw_destructor = true;
      }
      M_name_is_cdtor = true;
      eat_current();
      output += M_function_name;
      RETURN;
    }
    FAILURE;
  }

  // <unscoped-name> ::= <unqualified-name>             # Starts not with an 'S'
  //                 ::= St <unqualified-name>          # ::std::
  //
  // {anonymous}::
  bool demangler_ct::decode_unscoped_name(std::string& output)
  {
    if (current() == 'S')
    {
      if (next() != 't')
        FAILURE;
      eat_current();
      output += "std::";
    }
    decode_unqualified_name(output);
    RETURN;
  }

  // <name> ::= <nested-name>                                           # Starts with 'N'
  //        ::= <unscoped-name>                                         # Starts with 'S', 'C', 'D', a digit or a lower case character.
  //        ::= <unscoped-template-name> <template-args>                # idem
  //        ::= <local-name>                                            # Starts with 'Z'
  //
  // <unscoped-template-name> ::= <unscoped-name>
  //                          ::= <substitution>
  // {anonymous}::
  bool demangler_ct::decode_name(std::string& output, std::string& nested_name_qualifiers)
  {
    int substitution_start = M_pos;
    if (current() == 'S' && M_str[M_pos + 1] != 't')
    {
      if (!decode_substitution(output))
        FAILURE;
    }
    else if (current() == 'N')
    {
      decode_nested_name(output, nested_name_qualifiers);
      RETURN;
    }
    else if (current() == 'Z')
    {
      decode_local_name(output);
      RETURN;
    }
    else if (!decode_unscoped_name(output))
      FAILURE;
    if (current() == 'I')
    {
      // Must have been an <unscoped-template-name>.
      add_substitution(substitution_start, unscoped_template_name);
      if (!decode_template_args(output))
        FAILURE;
    }
    M_template_args_need_space = false;
    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
  //
  // {anonymous}::
  bool demangler_ct::decode_call_offset(std::string& output)
  {
    if (current() == 'h')
    {
      std::string dummy;
      eat_current();
      if (decode_number(dummy) && current() == '_')
      {
        eat_current();
        RETURN;
      }
    }
    else if (current() == 'v')
    {
      std::string dummy;
      eat_current();
      if (decode_number(dummy) && current() == '_')
      {
        eat_current();
        if (decode_number(dummy) && current() == '_')
        {
          eat_current();
          RETURN;
        }
      }
    }
    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
  //
  // {anonymous}::
  inline bool demangler_ct::decode_special_name(std::string& output)
  {
    if (current() == 'G')
    {
      if (next() != 'V')
        FAILURE;
      output += "guard variable for ";
      std::string nested_name_qualifiers;
      eat_current();
      if (!decode_name(output, nested_name_qualifiers))
        FAILURE;
      output += nested_name_qualifiers;
      RETURN;
    }
    else if (current() != 'T')
      FAILURE;
    switch(next())
    {
      case 'V':
        output += "vtable for ";
        eat_current();
        decode_type(output);
        RETURN;
      case 'T':
        output += "VTT for ";
        eat_current();
        decode_type(output);
        RETURN;
      case 'I':
        output += "typeinfo for ";
        eat_current();
        decode_type(output);
        RETURN;
      case 'S':
        output += "typeinfo name for ";
        eat_current();
        decode_type(output);
        RETURN;
      case 'c':
        output += "covariant return thunk to ";
        if (!decode_call_offset(output)
            || !decode_call_offset(output)
            || (M_pos += decode_encoding(M_str + M_pos, output)) < 0)
          FAILURE;
        RETURN;
      case 'C':         // GNU extention?
      {
        std::string first;
        output += "construction vtable for ";
        eat_current();
        if (!decode_type(first))
          FAILURE;
        while(isdigit(current()))
          eat_current();
        if (eat_current() != '_')
          FAILURE;
        if (!decode_type(output))
          FAILURE;
        output += "-in-";
        output += first;
        RETURN;
      }
      default:
        if (current() == 'v')
          output += "virtual thunk to ";
        else
          output += "non-virtual thunk to ";
        if (!decode_call_offset(output) || (M_pos += decode_encoding(M_str + M_pos, output)) < 0)
          FAILURE;
        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'.
  //
  // {anonymous}::
  int demangler_ct::decode_encoding(char const* in, std::string& output)
  {
    demangler_ct demangler(in);
    std::string nested_name_qualifiers;
    int saved_pos;
    demangler.store(saved_pos);
    if (demangler.decode_special_name(output))
      return demangler.M_pos;
    demangler.restore(saved_pos);
    std::string name;
    if (!demangler.decode_name(name, nested_name_qualifiers))
      return INT_MIN;
    if (demangler.current() == 0 || demangler.current() == 'E')
    {
      output += name;
      output += nested_name_qualifiers;
      return demangler.M_pos;
    }
    // Must have been a <function name>.
    if (demangler.M_name_is_template && !(demangler.M_name_is_cdtor || demangler.M_name_is_conversion_operator))
    {
      if (!demangler.decode_type(output))       // Return type of function
        return INT_MIN;
      output += ' ';
    }
    output += name;
    if (!demangler.decode_bare_function_type(output))
      return INT_MIN;
    output += nested_name_qualifiers;
    return demangler.M_pos;
  }

} // namespace

//
// demangle_symbol
//
// Demangle `input' and append to `output'.
// `input' should be a mangled_function_name as for instance returned
// by `libcw::debug::pc_mangled_function_name'.
//
void demangle_symbol(char const* input, std::string& output)
{

  if (input == NULL)
  {
    output += "(null)";
    return;
  }

  //
  // <mangled-name> ::= _Z <encoding>
  //
  int pos;
  if (input[0] != '_' || input[1] != 'Z' || (pos = demangler_ct::decode_encoding(input + 2, output) + 2) < 0 || input[pos] != 0)
    output.assign(input, strlen(input));        // Failure to demangle, return the mangled name.

}

//
// demangle_type
//
// Demangle `input' and append to `output'.
// `input' should be a mangled type as for returned
// by the stdc++ `typeinfo::name()'.
//
void demangle_type(char const* in, std::string& output)
{
  if (in == NULL)
  {
    output += "(null)";
    return;
  }
  demangler_ct demangler(in);
  if (!demangler.decode_type(output))
    output.assign(in, strlen(in));
}

  } // namespace debug
} // namespace libcw

#include <iostream>
using namespace ::libcw::debug;

char *demangle_type(const char *str)
{
std::string out;
::libcw::debug::demangle_type(str, out);
int n = out.size();
//char *result = new char[n+1];
static char *result = new char[200];
for (int i = 0; i < n; i++)
        result[i] = out[i];
result[n] = '\0';

return result;
}

#endif // __GXX_ABI_VERSION > 0

#endif // __GNUC__