/****************************************************************************
 *                                                                          *
 *                         GNAT COMPILER COMPONENTS                         *
 *                                                                          *
 *                            A D A D E C O D E                             *
 *                                                                          *
 *                          C Implementation File                           *
 *                                                                          *
 *           Copyright (C) 2001-2003, Free Software Foundation, Inc.        *
 *                                                                          *
 * GNAT is free software;  you can  redistribute it  and/or modify it under *
 * terms of the  GNU General Public License as published  by the Free Soft- *
 * ware  Foundation;  either version 2,  or (at your option) any later ver- *
 * sion.  GNAT is distributed in the hope that it will be useful, but WITH- *
 * OUT 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  distributed with GNAT;  see file COPYING.  If not, write *
 * to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, *
 * MA 02111-1307, USA.                                                      *
 *                                                                          *
 * As a  special  exception,  if you  link  this file  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 except- *
 * ion does not  however invalidate  any other reasons  why the  executable *
 * file might be covered by the  GNU Public License.                        *
 *                                                                          *
 * GNAT was originally developed  by the GNAT team at  New York University. *
 * Extensive contributions were provided by Ada Core Technologies Inc.      *
 *                                                                          *
 ****************************************************************************/

#ifdef IN_GCC
#include "config.h"
#include "system.h"
#else
#include <stdio.h>
#include <ctype.h>
#define ISDIGIT(c) isdigit(c)
#define PARMS(ARGS) ARGS
#endif

#include "adadecode.h"

static void add_verbose (const char *, char *);
static int has_prefix (const char *, const char *);
static int has_suffix (const char *, const char *);

/* This is a safe version of strcpy that can be used with overlapped
   pointers. Does nothing if s2 <= s1.  */
static void ostrcpy (char *s1, char *s2);

/* Set to nonzero if we have written any verbose info.  */
static int verbose_info;

/* Add TEXT to end of ADA_NAME, putting a leading " (" or ", ", depending
   on VERBOSE_INFO.  */

static void add_verbose (const char *text, char *ada_name)
{
  strcat (ada_name, verbose_info ? ", " : " (");
  strcat (ada_name, text);

  verbose_info = 1;
}

/* Returns 1 if NAME starts with PREFIX.  */

static int
has_prefix (const char *name, const char *prefix)
{
  return strncmp (name, prefix, strlen (prefix)) == 0;
}

/* Returns 1 if NAME ends with SUFFIX.  */

static int
has_suffix (const char *name, const char *suffix)
{
  int nlen = strlen (name);
  int slen = strlen (suffix);

  return nlen > slen && strncmp (name + nlen - slen, suffix, slen) == 0;
}

/* Safe overlapped pointers version of strcpy.  */

static void
ostrcpy (char *s1, char *s2)
{
  if (s2 > s1)
    {
      while (*s2) *s1++ = *s2++;
      *s1 = '\0';
    }
}

/* This function will return the Ada name from the encoded form.
   The Ada coding is done in exp_dbug.ads and this is the inverse function.
   see exp_dbug.ads for full encoding rules, a short description is added
   below. Right now only objects and routines are handled. There is no support
   for Ada types.

   CODED_NAME is the encoded entity name.

   ADA_NAME is a pointer to a buffer, it will receive the Ada name. A safe
   size for this buffer is: strlen (coded_name) * 2 + 60. (60 is for the
   verbose information).

   VERBOSE is nonzero if more information about the entity is to be
   added at the end of the Ada name and surrounded by ( and ).

     Coded name           Ada name                verbose info
  ---------------------------------------------------------------------
  _ada_xyz                xyz                     library level
  x__y__z                 x.y.z
  x__yTKB                 x.y                     task body
  x__yB                   x.y                     task body
  x__yX                   x.y                     body nested
  x__yXb                  x.y                     body nested
  xTK__y                  x.y                     in task
  x__y$2                  x.y                     overloaded
  x__y__3                 x.y                     overloaded
  x__Oabs                 "abs"
  x__Oand                 "and"
  x__Omod                 "mod"
  x__Onot                 "not"
  x__Oor                  "or"
  x__Orem                 "rem"
  x__Oxor                 "xor"
  x__Oeq                  "="
  x__One                  "/="
  x__Olt                  "<"
  x__Ole                  "<="
  x__Ogt                  ">"
  x__Oge                  ">="
  x__Oadd                 "+"
  x__Osubtract            "-"
  x__Oconcat              "&"
  x__Omultiply            "*"
  x__Odivide              "/"
  x__Oexpon               "**"     */

void
__gnat_decode (const char *coded_name, char *ada_name, int verbose)
{
  int lib_subprog = 0;
  int overloaded = 0;
  int task_body = 0;
  int in_task = 0;
  int body_nested = 0;

  /* Check for library level subprogram.  */
  if (has_prefix (coded_name, "_ada_"))
    {
      strcpy (ada_name, coded_name + 5);
      lib_subprog = 1;
    }
  else
    strcpy (ada_name, coded_name);

  /* Check for task body.  */
  if (has_suffix (ada_name, "TKB"))
    {
      ada_name[strlen (ada_name) - 3] = '\0';
      task_body = 1;
    }

  if (has_suffix (ada_name, "B"))
    {
      ada_name[strlen (ada_name) - 1] = '\0';
      task_body = 1;
    }

  /* Check for body-nested entity: X[bn] */
  if (has_suffix (ada_name, "X"))
    {
      ada_name[strlen (ada_name) - 1] = '\0';
      body_nested = 1;
    }

  if (has_suffix (ada_name, "Xb"))
    {
      ada_name[strlen (ada_name) - 2] = '\0';
      body_nested = 1;
    }

  if (has_suffix (ada_name, "Xn"))
    {
      ada_name[strlen (ada_name) - 2] = '\0';
      body_nested = 1;
    }

  /* Change instance of TK__ (object declared inside a task) to __.  */
  {
    char *tktoken;

    while ((tktoken = (char *) strstr (ada_name, "TK__")) != NULL)
      {
	ostrcpy (tktoken, tktoken + 2);
	in_task = 1;
      }
  }

  /* Check for overloading: name terminated by $nn or __nn.  */
  {
    int len = strlen (ada_name);
    int n_digits = 0;

    if (len > 1)
      while (ISDIGIT ((int) ada_name[(int) len - 1 - n_digits]))
	n_digits++;

    /* Check if we have $ or __ before digits.  */
    if (ada_name[len - 1 - n_digits] == '$')
      {
	ada_name[len - 1 - n_digits] = '\0';
	overloaded = 1;
      }
    else if (ada_name[len - 1 - n_digits] == '_'
	     && ada_name[len - 1 - n_digits - 1] == '_')
      {
	ada_name[len - 1 - n_digits - 1] = '\0';
	overloaded = 1;
      }
  }

  /* Change all "__" to ".". */
  {
    int len = strlen (ada_name);
    int k = 0;

    while (k < len)
      {
	if (ada_name[k] == '_' && ada_name[k+1] == '_')
	  {
	    ada_name[k] = '.';
	    ostrcpy (ada_name + k + 1, ada_name + k + 2);
	    len = len - 1;
	  }
	k++;
      }
  }

  /* Checks for operator name.  */
  {
    const char *trans_table[][2]
      = {{"Oabs", "\"abs\""},  {"Oand", "\"and\""},    {"Omod", "\"mod\""},
	 {"Onot", "\"not\""},  {"Oor", "\"or\""},      {"Orem", "\"rem\""},
	 {"Oxor", "\"xor\""},  {"Oeq", "\"=\""},       {"One", "\"/=\""},
	 {"Olt", "\"<\""},     {"Ole", "\"<=\""},      {"Ogt", "\">\""},
	 {"Oge", "\">=\""},    {"Oadd", "\"+\""},      {"Osubtract", "\"-\""},
	 {"Oconcat", "\"&\""}, {"Omultiply", "\"*\""}, {"Odivide", "\"/\""},
	 {"Oexpon", "\"**\""}, {NULL, NULL} };
    int k = 0;

    while (1)
      {
	char *optoken;

	if ((optoken = (char *) strstr (ada_name, trans_table[k][0])) != NULL)
	  {
	    int codedlen = strlen (trans_table[k][0]);
	    int oplen = strlen (trans_table[k][1]);

	    if (codedlen > oplen)
	      /* We shrink the space.  */
	      ostrcpy (optoken, optoken + codedlen - oplen);
	    else if (oplen > codedlen)
	      {
		/* We need more space.  */
		int len = strlen (ada_name);
		int space = oplen - codedlen;
		int num_to_move = &ada_name[len] - optoken;
		int t;

		for (t = 0; t < num_to_move; t++)
		  ada_name[len + space - t - 1] = ada_name[len - t - 1];
	      }

	    /* Write symbol in the space.  */
	    strncpy (optoken, trans_table[k][1], oplen);
	  }
	else
	  k++;

	/* Check for table's ending.  */
	if (trans_table[k][0] == NULL)
	  break;
      }
  }

  /* If verbose mode is on, we add some information to the Ada name.  */
  if (verbose)
    {
      if (overloaded)
	add_verbose ("overloaded", ada_name);

      if (lib_subprog)
	add_verbose ("library level", ada_name);

      if (body_nested)
	add_verbose ("body nested", ada_name);

      if (in_task)
	add_verbose ("in task", ada_name);

      if (task_body)
	add_verbose ("task body", ada_name);

      if (verbose_info == 1)
	strcat (ada_name, ")");
    }
}

char *
ada_demangle (const char *coded_name)
{
  char ada_name[2048];

  __gnat_decode (coded_name, ada_name, 0);
  return xstrdup (ada_name);
}
