/****************************************************************************
**
**  Name:  LMANGLE.CPP
**
**  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_186/loader/lmangle.cpv  $
** 
**    Rev 1.1   26 Feb 1997 11:49:58   Judy
** 
**    Rev 1.0   14 Jun 1996 17:25:42   Judy
** Initial revision.
** 
**    Rev 1.2   18 Aug 1994 11:32:10   nghia
** Removed warning on C++ duplicate symbols.
** 
**    Rev 1.1   17 Jun 1994 11:39:34   nghia
** Revised to use the loader global error reporting. 
** 
**    Rev 1.0   20 May 1994 11:01:24   nghia
** Initial revision.
** 
**  
**  $Header:   S:/tbird/mt2_186/loader/lmangle.cpv   1.1   26 Feb 1997 11:49:58   Judy  $
**
**  Copyright (C) 1994 Microtek International.  All rights reserved.
**
*****************************************************************************/

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

#ifndef _BASEWIND_
#include "basewind.h"
#endif

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif

#ifndef _HEAP_
#include "heap.h"
#endif

#ifndef _LOADER_
#include "loader.h"
#endif

#ifndef _LMANGLE_
#include "lmangle.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 */

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

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

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

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

/****************************************************************************
**
**  LdrCppFixupLocal
**
*****************************************************************************/
RETCODE EXPORT LdrCppFixupLocal(LPSTR pname, LPSTR *puserName) {
   // Strip off mangling prefix.  For locals, the prefix indicates
   // lexical level.
   if (strlen(pname) == 0) {    // check for null name 
      *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;
}

/****************************************************************************
**
**  LdrCppFixupFunction
**
*****************************************************************************/
RETCODE EXPORT LdrCppFixupFunction(LPSTR pname, LPSTR *puserName,
                                   BOOLEAN firstTimeLoad) {
   CHAR *pos, *pclassName;
   CHAR lengthBuffer[5];  // allows for 5-digit length   
   U16 nlength;
   BOOLEAN ctorFlag = FALSE, dtorFlag = FALSE;
   LOOP_VAR i;
   FNNODE *fnPtr;
   RETCODE err = GOOD;
   CHAR pmangledName[MAX_STRING_SIZE];


   // Strip off function/class signature suffix, returns pointer to demangled
   // name (original name preserved).
   //
   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 (firstTimeLoad) {
      // 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 just adds to sym table.
      //
      err = CppAddFunction(pname, &fnPtr);
      // duplicate detected, hence restore the  mangled name
      // 08/17/94 - Nghia
      // do not report this warning.
      if (err == ER_CPP_DUPLICATE) 
         strcpy(*puserName, pmangledName);
   }
   return err;
}

/****************************************************************************
**
**  LdrCppFixupGlobal
**
*****************************************************************************/
RETCODE EXPORT LdrCppFixupGlobal(LPSTR pname, LPSTR *puserName) {
   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;
}

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

   if (strlen(pname) == 0) {    // Check for null name 
      *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;
}

/****************************************************************************
**
**  LdrCppClearFunctionTable
**
*****************************************************************************/
RETCODE EXPORT LdrCppClearFunctionTable(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;
   return GOOD;
}

/******************************** LOCAL FUNCTION ***************************/

/****************************************************************************
**
**  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;
}
/******************************** E O F ***********************************/
