// C++ IA64 / g++ v3 demangler  -*- C++ -*-

// Copyright (C) 2003 Free Software Foundation, Inc.
// Written by Carlo Wood <carlo@alinoe.com>
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING.  If not, write to the Free
// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.

// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.

#include <cxxabi.h>
#include <bits/demangle.h>

// __cxa_demangle
//
// Demangle a C++ symbol or type name.
//
// `mangled-name' is a pointer to a null-terminated array of characters.
// It may be either an external name, i.e. with a "_Z" prefix, or an
// internal NTBS mangling, e.g. of a type for type_info.
//
// `buf' may be null.  If it is non-null, then n must also be non-null,
// and buf is a pointer to an array, of at least *n characters, that
// was allocated using malloc.
//
// `status' points to an int that is used as an error indicator. It is
// permitted to be null, in which case the user just doesn't get any
// detailed error information. 
//
// Returns: a pointer to a null-terminated array of characters, the
//          demangled name.  Or NULL in case of failure.
//
// If there is an error in demangling, the return value is a null pointer.
// The user can examine *status to find out what kind of error occurred.
// Meaning of error indications:
//
//     *  0: success
//     * -1: memory allocation failure
//     * -2: invalid mangled name
//     * -3: invalid arguments (e.g. buf nonnull and n null) 
//

namespace __cxxabiv1 
{
  namespace 
  {
    char* const error = 0;

    enum status_codes 
    {
      success = 0,
      memory_allocation_failure = -1,
      invalid_mangled_name = -2,
      invalid_argument = -3
    };

    inline char*
    failure(status_codes error_code, int* status)
    {
      if (status)
	*status = error_code;
      return error;
    }

    char*
    finish(char const* demangled_name, size_t demangled_name_size,
	   char* buf, size_t* n, int* status)
    {
      if (!buf || *n < demangled_name_size + 1)
      {
	if (n)
	  *n = demangled_name_size + 1;
	buf = (char*)realloc(buf, demangled_name_size + 1);
	if (!buf)
	  return failure(memory_allocation_failure, status);
      }
      if (status)
	*status = success;
      std::strncpy(buf, demangled_name, demangled_name_size);
      buf[demangled_name_size] = 0;
      return buf;
    }
  } // namespace

  char*
  __cxa_demangle(char const* mangled_name, char* buf, std::size_t* n, 
		 int* status)
  {
    using namespace __gnu_cxx;
    typedef demangler::session<std::allocator<char> > session_type;

    if (!mangled_name || (buf && !n))
      return failure(invalid_argument, status);

    std::string result;
    if (mangled_name[0] == '_')		
    {
      // External name?
      if (mangled_name[1] == 'Z')		
      {
	// C++ name?
	int cnt = session_type::
	    decode_encoding(result, mangled_name + 2, INT_MAX);
	if (cnt < 0 || mangled_name[cnt + 2] != 0)
	  return failure(invalid_mangled_name, status);
	return finish(result.data(), result.size(), buf, n, status);
      }
      else if (mangled_name[1] == 'G')	
      {
	// Possible _GLOBAL__ extension?
	if (!std::strncmp(mangled_name, "_GLOBAL__", 9) 
	    && (mangled_name[9] == 'D' || mangled_name[9] == 'I')
	    && mangled_name[10] == '_')
	{
	  if (mangled_name[9] == 'D')
	    result.assign("global destructors keyed to ", 28);
	  else
	    result.assign("global constructors keyed to ", 29);
	  // Output the disambiguation part as-is.
	  result += mangled_name + 11;
	  return finish(result.data(), result.size(), buf, n, status);
	}
      }
    }

    // Ambiguities are possible between extern "C" object names and
    // internal built-in type names, e.g. "i" may be either an object
    // named "i" or the built-in "int" type.  Such ambiguities should
    // be resolved to user names over built-in names.  Builtin types
    // are any single lower case character.  Any other single
    // character is not a mangled type so we can treat those the same
    // here.
    if (mangled_name[1] == 0)
      return finish(mangled_name, 1, buf, n, status);

    // Not a built-in type or external name, try to demangle input as
    // NTBS mangled type name.
    session_type demangler_session(mangled_name, INT_MAX);
    if (!demangler_session.decode_type(result) 
	|| demangler_session.remaining_input_characters())
    {
      // Failure to demangle, assume extern "C" name.
      result = mangled_name;		
    }
    return finish(result.data(), result.size(), buf, n, status);
  }
} // namespace __cxxabiv1