/****************************************************************************
**
**  Name:  LMANGLE.C
**
**  Description:
**    Support routines for C++.  Currently supports MRI C++.
**    Note that to support other C++ implementations/toolchains, this module
**    should be replaced OR made runtime switchable with the demangling
**    routines appropriate to the new toolchain.
**
**    MRI mangling algorithm:  functions - see ARM first edition.
**                             locals - use lexical level prefix on name
**                             globals - not mangled
**    Note that MRI allows (per their user documentation) *any* identifier
**    with double underscores embedded to be 'reserved' to the implementation
**    (hence users will use such names at their own risk, and will not be
**    warned either).
**
**  $Log:   S:/tbird/mt2_68k/l695/lmangle.c_v  $
** 
**    Rev 1.0   13 Feb 1997 08:50:56   gene
** Initial revision.
** 
**    Rev 1.0   07 Sep 1995 10:32:18   gene
** Initial revision.
** 
**    Rev 1.5   01 Mar 1994 09:11:10   nghia
** Fixed bug in PV2.1: Loader demangle function turned a __x__ into a null 
** string.  - Revised to detect the return string is not a null string.
** 
**    Rev 1.4   03 Aug 1993 17:42:18   nghia
** Removed LERR_xxxx to use standard error codes.
** 
**    Rev 1.3   26 Jul 1993 15:14:54   courtney
** Corrected incorrect condition in CppFixupMember due to release stress..
** 
**    Rev 1.2   26 Jul 1993 14:29:40   courtney
** Check for null member name (no mangling), which occurs on unnamed
** bitfields and messes up the symbol table.
** 
**    Rev 1.1   14 Jun 1993 11:21:24   nghia
** Removed TBIRDWARN() call.
** 
**    Rev 1.0   06 Jun 1993 21:23:52   courtney
** Initial revision.
**  
**  $Header:   S:/tbird/mt2_68k/l695/lmangle.c_v   1.0   13 Feb 1997 08:50:56   gene  $
**
**  Copyright (C) 1991-93 Microtek International.  All rights reserved.
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#include <ctype.h>
#include <stdlib.h>

#ifndef __HEAP__
#include "heap.h"
#endif
#ifndef __LDR__
#include "ldr.h"
#endif
#ifndef __ERR__
#include "err.h"
#endif
#ifndef __LMANGLE__
#include "lmangle.h"
#endif
#ifndef __LFLAGS__
#include "lflags.h"
#endif
                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/

/* These are used for duplicate detection, in adding static procedures to
   the symbol table. */
STATIC FNNODE *fnTable; /* pointer to head of list */
STATIC U16 fnCount;     /* count of nodes in list */
STATIC FNNODE *fnLast;  /* pointer to last node with information */
STATIC U16 mangleWarn = TRUE;  /* defaults to true */

                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/

RETCODE PRIVATE CppAddFunction(LPSTR pname, FNNODE **fnReturnPtr);
RETCODE PRIVATE CppLookupFunction(LPSTR pname, FNNODE **fnReturnPtr);

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/

/****************************************************************************
**
**  CppFixupLocal
**
*****************************************************************************/
RETCODE CppFixupLocal(LPSTR pname, LPSTR *puserName) {
/* pname - pointer to mangled name
   puserName - pointer to address of user name
*/
   /* Strip off mangling prefix.  For locals, the prefix indicates
   ** lexical level.
   */

   /* check for null name */
   if (strlen(pname) == 0) {
      *puserName = pname;  /* pointer assign */
      return (GOOD);
   }
   /* Traverse till we hit first character of identifier name */
   while (!isalpha(*pname))  
      ++pname;
   /* special case of 'this' pointer */
   if (strncmp(pname, CPP_THIS, strlen(CPP_THIS)) == 0) {
      ++pname;  /* advance past leading 'X' */
      pname[strlen(CPP_USERTHIS)] = '\0';  /* strip off mangling at end */
   }
   *puserName = pname;  /* pointer assign */

   return (GOOD);
}

/****************************************************************************
**
**  CppFixupFunction
**
*****************************************************************************/
RETCODE CppFixupFunction(LPSTR pname, LPSTR *puserName, BOOL oflag) {
/* pname - pointer to mangled name (const)
   puserName - pointer to address of user name
   oflag - creation flag:
      if MCREATE, symbol will be created for entry to symbol table, 
         check duplicates and add to duplicate list in loader
      if MOPEN, we're in ondemand callback to load local symbols.  This is
         a lookup only operation, no duplicate checking needed, just return
         the demangled name.
      Note that duplicate detection is ONLY used for initial load, never
      for ondemand load.  Functions that can have duplicates due to the
      mangled -> demangle operation will be loaded during the initial load
      as static procedures.
*/
   CHAR *pos;
   CHAR lengthBuffer[5];  /* allows for 5-digit length */
   U16 nlength;
   BOOLEAN ctorFlag = FALSE, dtorFlag = FALSE;
   LOOP_VAR i;
   CHAR *pclassName;
   FNNODE *fnPtr;
   RETCODE rtn;
   CHAR pmangledName[IEEE_IDNLEN];

   /* Strip off function/class signature suffix, returns pointer to demangled
   ** name (original name preserved).
   */

   /* check for null name */
   if (strlen(pname) == 0) {
      *puserName = pname;  /* pointer assign */
      return (GOOD);
   }

   /* save mangled name (in case duplicate) */
   strcpy(pmangledName, pname);

   /* check for ctor/dtors, flag needed later for dtor fixup */
   if (strncmp(pname, CPP_CTOR, strlen(CPP_CTOR)) == 0)
      ctorFlag = TRUE;
   if (strncmp(pname, CPP_DTOR, strlen(CPP_DTOR)) == 0)
      dtorFlag = TRUE;
   if (ctorFlag || dtorFlag) {
      pname += strlen(CPP_CTOR);  /* generic pointer advance */
      /* pull ascii length */
      for (i=0; isdigit(*pname); i++)
         lengthBuffer[i] = *pname++;
      lengthBuffer[i] = '\0';
      nlength = atoi(lengthBuffer);
      /* advance past classname (and for publics, embedded underscores):
         global ctor format:  __ct__5VEC_AFv
         public ctor format:  __ct__7__VEC_AFv
      */
      pclassName = pname;  /* pointer assign */
      pname += nlength;
      *pname = '\0';  /* strips off function signature */
      pname = pclassName;  /* pointer assign */
      /* special dtor fixup */
      if (dtorFlag) {
         --pname;  /* this is legit since dt prefix seen */
         *pname = CPP_DTOR_CHAR;
      }
      *puserName = pname;  /* pointer assign */
      /* fall thru: check for dup ctors */
   } else {  /* normal function */
      /* Traverse till we hit function signature (double-underscore),
         hack the rest off.  Note that compiler-generated ctor/dtors as
         well as new/delete have leading *triple* underscore (only the
         first gets stripped as a result of public name 'fixup'.).  Since
         there must be an alpha in the name, its safe to start looking
         at the [1] position.
      */
      pos = strstr(pname+1, MANGLE_DELIMETER);
      if (pos != NULL)
         *pos = '\0';
      *puserName = pname;  /* pointer assign */
   }

   /* Static initializer/destructor (for modules).  Module signature
      has already been stripped above, here we strip leading underscores.
   */
   if ((strncmp(pname, CPP_STATICI, strlen(CPP_STATICI)) == 0) ||
       (strncmp(pname, CPP_STATICD, strlen(CPP_STATICD)) == 0)) {
      /* traverse past leading underscores */
      while (!isalpha(*pname))
         ++pname;
      *puserName = pname;  /* pointer assign */
   }

   if (oflag == MCREATE) {
      /* Detect duplicate user symbol name (mangle -> demangle operation
      ** can create duplicates where none previously existed in loadfile).
      ** Strategy: on initial load, clear static count in this file
      ** for function symbol fixup, add item to list check for duplicate
      ** by lookup prior to return.  If found in list (user name duplicate),
      ** return username is pointer to mangled name.  This routine encapsulates
      ** all this, the caller (i.e., in ldebug.c) just adds to sym table.
      */
      rtn = CppAddFunction(pname, &fnPtr);
      if (rtn != ER_CPP_DUPLICATE && rtn != GOOD)
         return (rtn);
      if (rtn == ER_CPP_DUPLICATE) {
         /* duplicate detected, hence return mangled name */
         strcpy(*puserName, pmangledName);
         if (mangleWarn) {
            /* once for duration of load, warn user of duplicates */
            Warning(ER_CPP_DUPLICATE);
            mangleWarn = FALSE;
         }
      }
   }

   return (GOOD);
}

/* Normally, globals don't need name demangling (with MRI),
** but for Phase 2 of C++ support (e.g., initial not full level of
** support), we kick out vtables and things, so return error to caller.
** We keep the same interface as other demangling routines in this
** module, to allow this function interface to be used by other toolchains
** which may use global name mangling.
*/
/****************************************************************************
**
**  CppFixupGlobal
**
*****************************************************************************/
#pragma argsused
RETCODE CppFixupGlobal(LPSTR pname, LPSTR *puserName) {
/* pname - pointer to mangled name
   puserName - pointer to address of user name
*/
   CHAR *pos;

   /* Demangle vtables and vtables.  All other globals are not mangled. */
   /* Vtables (virtual function tables) and ptables (pointers to?)
      generated by compiler.  Found in module globals, as well as publics.
      Since all vtbls have the same name prefix, there are likely to be lots
      of duplicates, hence mangled names stored in the symbol table.
      Users will not want to debug the virtual function tables, probably.
      Note that the Loader does not need to keep track of duplicate globals
      data symbols; the symbol table does this.
   */
   if ((strncmp(pname, CPP_VTBL, strlen(CPP_VTBL)) == 0) ||
       (strncmp(pname, CPP_PTBL, strlen(CPP_PTBL)) == 0)) {
      /* traverse past leading underscores */
      while (!isalpha(*pname))
         ++pname;
   }
   /* Strip trailing class and module signature */
   if (((pos = strstr(pname, MANGLE_DELIMETER)) != NULL) &&
      (pos != pname)) {  
      /*
      ** NOTES: 01/18/94 - Nghia
      ** for string __xxx__ (not a c++ symbol), make sure not to truncate
      ** the symbol to a NULL string.  Else chop off the string at the
      ** first occurence of the "__".
      */
      *pos = '\0';
   }
   
   *puserName = pname;  /* pointer assign */
   return (GOOD);
}

/****************************************************************************
**
**  CppFixupMember
**
*****************************************************************************/
RETCODE CppFixupMember(LPSTR pname, LPSTR *puserName)
{
   CHAR *pos;

   /* Check for null name */
   if (strlen(pname) == 0) {
      *puserName = pname;
      return(GOOD);
   }
   /* Skip leading underscores (vtables and things) */
   while (!isalpha(*pname))
      ++pname;
   /* Traverse to class signature, hack the rest off to leave only a pure
      user name. */
   if (((pos = strstr(pname, MANGLE_DELIMETER)) != NULL) &&
       (pos != pname)) {
      *pos = '\0';
   }
   *puserName = pname;  /* pointer assign */

   return (GOOD);
}

/****************************************************************************
**
**  CppAddFunction
**
**  Description:
**    Add static function user name to list, for duplicate detection.
**
**  Parameters:
**        pname (in):         demangled user name
**        fnReturnPtr (out):  pointer to function node found/allocated
**
**  Return:
**        ER_CPP_DUPLICATE  if name already on list
**
*****************************************************************************/
RETCODE PRIVATE CppAddFunction(LPSTR pname, FNNODE **fnReturnPtr)
{
   FNNODE *fnPtr;

   /* check for a name thats been mangled to dust */
   if (strlen(pname) == 0)
      return (GOOD);
   CppLookupFunction(pname, &fnPtr);
   if (fnPtr == NULL) {
      /* not on chain, allocate new node */
      if ((fnPtr = (FNNODE *)TMalloc(sizeof(FNNODE))) == NULL)
         return (ER_NO_MEMORY);
      strcpy(fnPtr->fnName, pname);
      fnPtr->fnNext = NULL;
      /* link to existing chain */
      if (fnTable == NULL) {
         /* check for first entry */   
         fnTable = fnPtr;
         fnLast = fnTable;
      } else {
         fnLast->fnNext = fnPtr;
         fnLast = fnPtr;
      }
      *fnReturnPtr = fnPtr;  /* pointer assign */
      return (GOOD);
   }
   /* duplicate */
   *fnReturnPtr = fnPtr;  /* pointer assign */   
   return (ER_CPP_DUPLICATE);
}

/****************************************************************************
**
**  CppLookupFunction
**
**  Description:
**    Lookup given function name by traversing list of function names
**    for this module scope.
**
**  Parameters:
**        pname (in):         demangled user name
**        fnReturnPtr (out):  pointer to function node found, else NULL
**
*****************************************************************************/
RETCODE PRIVATE CppLookupFunction(LPSTR pname, FNNODE **fnReturnPtr)
{
   FNNODE *fnP = fnTable;

   *fnReturnPtr = NULL;
   while (fnP != NULL) {
      if (strcmp(pname, fnP->fnName) == 0) {
         /* duplicate found */
         *fnReturnPtr = fnP;  /* pointer assign */
         break;
      } else {
         fnP = fnP->fnNext;
      }
   }
   return (GOOD);
}

/****************************************************************************
**
**  CppClearFunctionTable
**
**  Description:
**    Clear function list constructed for module.
**
*****************************************************************************/
RETCODE CppClearFunctionTable(VOID)
{
   FNNODE *fnP, *fnPrev;

   /* TFree all links, and allocated string memory */
   fnP = fnTable;
   while (fnP != NULL) {
      fnPrev = fnP;
      fnP = fnP->fnNext;
      TFree((LPSTR)fnPrev);
   }
   fnTable = fnLast = NULL;
   fnCount = 0;

   /* reset warning on for mangled names */
   mangleWarn = TRUE;

   return (GOOD);
}


/******************************** E O F ***********************************/
