
/***************************************************************************
**
**    $Header$
**
**    $Log$
** 
****************************************************************************/

/****************************************************************************
**
**  Name:  symparse.cpp
**
**  Description:
**     This module provides functions which parse an input string and returns
**        the address of the symbol, if found.
**
**  Status:  PRELIMINARY
**
**  $Log$
** 
**  $Header:   D:/EPSLDV1/SRC/LOG/SYMPARSE.CPP   1.7.1.0.1.0   11 Nov 1996 12:57:06   ZJRD  $
**
**  Copyright (C) 1995 Microtek International.  All rights reserved.
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/

#include "stdafx.h"                      
#include <stdlib.h>
#include <string.h>

#include "symblsvr.h"
#include "symutil.h"
#include "symget.h"
#include "symerrs.h"

//#ifdef __cplusplus
//extern "C" {
//#endif

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/
#define INVALID 0
#define STR_CHR 1
#define NUM_CHR 2
#define SPECIAL 3

#define MAX_SUB_SYMBOLS 8

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


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


                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/
//extern RETCODE StkGetCurrentFunction(SYM_DESCRIPTOR *currentFunction);
//extern RETCODE StkGetCurrentModule(SYM_DESCRIPTOR *currentModule);

   /* map characters to white space, string char, or special chars:
      0 = invalid, 1 = string char, 2 = 0-9, 3 = special */
PRIVATE const  U8 charMap[128] =
      { 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,  /* control */
        0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,  /* control */
        0, 0, 0, 3, 1, 0, 0, 1,  0, 0, 0, 0, 0, 0, 0, 3,  /* punctuation */
        2, 2, 2, 2, 2, 2, 2, 2,  2, 2, 0, 0, 0, 0, 0, 1,  /* nums, punc */
        0, 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1, 1, 1,  /* caps */
        1, 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 0, 3, 0, 0, 1,  /* caps punc */
        1, 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1, 1, 1,  /* lower case */
        1, 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 0, 0, 0, 1, 0 };/* lower,brackets */

//--------------------------------------------------------------------------
// SymGetAddrFromName
//
// Design:
//    scan string
//       replace # with \0 except if first char
//       build array of long pointers to start of each name or number
//       keep counter of how many found
//       if illegal character, return ER_SYM_INVALID_CHAR
//    if data type, error (!!!@@@ needs to be completed)
//    if code, return error if variable type found
//    1 param
//       num (line) (assume mod context)
//       funcOrLabel (assume mod context)
//       var        (assume mod context, mod/func context, global context)
//       module
//    2 params
//       num (line), num (col)
//       mod, num (line)
//       mod, funcOrLabel
//       mod, var    [not implemented]
//       func, label (assume mod context) [not implem]
//       func, var   (assume mod context) [not implem]
//    3 params
//       mod, num (line), num (col)
//       mod, func, label [not implem]
//       mod, func, var   [not implem]                       
//
//    Modify this function by Gates Hua , 9/7/95 for bug 0007 
//
//------------------------------------------------------------------------
RETCODE WINAPI
SymGetAddrFromName(LPSTR                 nameStr,
                   BASIC_SYM_ADDR_TYPE   requestedAddrType, 
                   SYM_DESCRIPTOR    FAR *lpSymFoundDesc,
                   ADDR_RANGE_TYPE   FAR *lpAddrRange,
                   LINENUM_TYPE      FAR *lpActualLinenum,
                   COLUMN_RANGE_TYPE FAR *lpActualColumnRange) {

   RETCODE err;

   typedef struct {
      LPSTR   argvStrPtr;
      BOOLEAN isName;
      BOOLEAN hasPath;
   } ARG_TYPE;

   ARG_TYPE       argv[MAX_SUB_SYMBOLS];
   U8             argc;
   LPSTR          locPtr;
   SYM_DESCRIPTOR currentModule, currentFunc, foundSymbol, currentContext;
   SYM_DESCRIPTOR tmpSymbol;
   SYM_TYPE_TYPE  symType;
   BOOLEAN        isGlobal;
   U32            tmpU32;
   LINENUM_TYPE   linenum;
   COLUMN_TYPE    column;
   S8             *endPtr;
   LINENUM_DESCRIPTOR nextIndex;
   BOOLEAN        modFound = FALSE;
   BOOLEAN        funcFound = FALSE;    
   // Add by Gates Hua , 9/7/95 for bug 0007 
   S8             nullStr[1];

   // initialize return params
   *lpActualLinenum = 0;
   lpActualColumnRange->columnStart = 0;   // zeroing structure
   lpActualColumnRange->columnEnd = 0;

   locPtr = nameStr;
   argc = 0;
   nullStr[0] = '\0';

   if (*locPtr != '#')
      return ER_SYM_SYM_START_NOT_SHARP;
   while (*locPtr != '\0')  {
      switch (charMap[(*locPtr) & 0x7F])  {
         case STR_CHR:
            argv[argc-1].isName = TRUE;  // in case name starts with number
            // fall thru and incr locPtr

         case NUM_CHR:
            locPtr++;
            break;

         case SPECIAL:
            if (*locPtr == '#')  {
               argc++;
               if (argc > MAX_SUB_SYMBOLS) //went past limit of nested symbols
                  return ER_CLI_SYNTAX;
               *locPtr = '\0';   // insert null to form sub-strings
               locPtr++;
               argv[argc-1].argvStrPtr = locPtr;
               if (*locPtr == '#')  {
                  return ER_SYM_INVALID_CHAR;  // double ## not allowed

               } else if (charMap[(*locPtr) & 0x7F] == NUM_CHR) { //lookahead
                  argv[argc-1].isName = FALSE;

               } else {
                  argv[argc-1].isName = TRUE;
               }
               argv[argc-1].hasPath = FALSE;  // default to no path
            } else {  // only other valid chars are path chars
               argv[argc-1].hasPath = TRUE;
               locPtr++;
            }
            break;

         default:   // includes case INVALID
            return ER_SYM_INVALID_CHAR;
      
      } // end of switch
   } // end of while

   // default the symbol search type to unknown
   symType = SYM_UNDEFINED;

   // fill in current module; may override if user entered mod name
   if (GOOD != (err = StkGetCurrentModule(&currentModule)))
      goto CLEANUP;
   if (GOOD != (err = StkGetCurrentFunction(&currentFunc)))
      goto CLEANUP;

//   if ((err = SymGetModuleListHead(&currentModule)) != GOOD)
//      return err;                        
//   currentFunc = NULL;

//#ifdef _ZLR_
   
//   if ( ICE_OK != AbiGetOneReg( 0 , &pc_value) )
//      return ( ER_SYM_SYM_START_NOT_SHARP );
   
//#endif

   // NOTE: arguments are in reverse order in argv; i.e. left-most param
   // is in argv[0].  The last param is in argv[argc-1]   
   switch (argc)  {
      case 3:
         if (argv[argc-3].isName) { // only module valid input
            // !!! needs fixup for module path
            if (GOOD != (err =
               SymGetModuleDesc(argv[argc-3].argvStrPtr,
                                (LPSTR)nullStr,
                                &currentModule)))
               goto CLEANUP;
            UtilOnDemandLoad(currentModule);
            modFound = TRUE;
         } else {
           err = ER_CLI_SYNTAX;
           goto CLEANUP;
         }
         // fall thru to case 2:

      case 2:  // second param can be module, function, or number
               // precedence is function (if mod context) then module
               // if function not found
         if (argv[argc-2].isName) {
            if (currentModule != NULL_SYMBOL) { // mod context exists;
                                                // search for child function
               // Get module child and search
               if (GOOD != (err =
                  SymGetSymbolChild(currentModule, &symType,
                                    &currentContext)))
                  goto CLEANUP;
               if (symType == NULL_SYMBOL) {
                  // while this code has a "bug" in that it should go test
                  // for the input as a module (and not return an error at
                  // this point), it is very unlikely that since
                  // the currentModule context is not null, this
                  // module (or any module) will have at least one function
                  // child.  Therefore, this IF branch should never be taken.
                  err = ER_SYMBOL_NOT_FOUND;
                  goto CLEANUP;
                  }
               symType = SYM_FUNCTION;
               err =
                  SymGetSymbolFromContext(currentContext,
                                          argv[argc-2].argvStrPtr,
                                          &symType,  // both input and output
                                          &foundSymbol,
                                          &isGlobal);
               if (ER_SYMBOL_NOT_FOUND == err) {
                  // condition: second param as function not found with
                  // present module context; treat second param as module
                  // name and continue parse.
                  symType = SYM_UNDEFINED;  // set so will take next "else"
               } else if (GOOD != err) {
                  goto CLEANUP;
               }
               // fall thru if function found or not found (but not other err)
               if (SYM_FUNCTION == symType) {
                  currentFunc = foundSymbol;
                  funcFound = TRUE;
                  
                  if (GOOD != (err = 
                     SymGetParentModule(currentFunc, &tmpSymbol)))
                     goto CLEANUP;
                  if ( !modFound ) {  
                     // fill in current module
                     currentModule = tmpSymbol;
                  }
                  else if ( tmpSymbol != currentModule ) {
                     err = FAILURE;
                     goto CLEANUP;
                  }
               } else {  // if mod not already entered, search for it
                  if (modFound) {
                     err = ER_SYMBOL_NOT_FOUND;
                     goto CLEANUP;
                  } else{ 
               // !!! needs fixup to include search extraction of module path
                     if (GOOD != (err =
                        SymGetModuleDesc(argv[argc-2].argvStrPtr,
                                         (LPSTR)nullStr,
                                         &currentModule))) {
                        goto CLEANUP;
                     }
                     modFound = TRUE;
                  }
               }
            } else { // no current module context; symbol can be a module
                     // or global function followed by symbol
               // !!! needs fixup to include search extraction of module path
               err = SymGetModuleDesc(argv[argc-2].argvStrPtr,
                                      (LPSTR)nullStr,
                                      &currentModule);
               if (GOOD == err) {
                  UtilOnDemandLoad(currentModule);
                  modFound = TRUE;
                  // fall thru to case 1:
               } else if (ER_SYMBOL_NOT_FOUND == err)  {
                  // check for global function
                  err = SymGetSymbolFromContext(NULL_SYMBOL,
                                                argv[argc-2].argvStrPtr,
                                                &symType,
                                                &foundSymbol,
                                                &isGlobal);
                  if ((GOOD == err) && (SYM_FUNCTION == symType)) {
                     currentFunc = foundSymbol;
                     funcFound = TRUE;
                     // fill in current module
                     if (GOOD != (err = 
                        SymGetParentModule(currentFunc, &tmpSymbol)))
                        goto CLEANUP;
                     currentModule = tmpSymbol;
                  } else {
                     goto CLEANUP;
                  }
               } else {
                  goto CLEANUP;
               }
            }
            // fall thru to case 1:

         } else { // must be #line#column
            if (SYM_DATA_ADDR == requestedAddrType) {
               // if data requested, can't have line/column inputs
               err = ER_SYM_NOT_DATA_ADDRESS;
               goto CLEANUP;
            }
            if (currentModule == NULL_SYMBOL) {
               err = ER_SYM_NO_CURRENT_CONTEXT;
               goto CLEANUP;
               }
            // oonvert linenum; decimal input only
            tmpU32 = strtoul(argv[argc-2].argvStrPtr, &endPtr, 10);
            if ((*endPtr != 0) || (tmpU32 > 0xFFFFL)) {
               err = ER_CLI_SYNTAX;
               goto CLEANUP;
               }
            linenum = (LINENUM_TYPE)tmpU32;

            // now convert column; decimal input only
            tmpU32 = strtoul(argv[argc-1].argvStrPtr, &endPtr, 10);
            if ((*endPtr != 0) || (tmpU32 > 0xFFL)) {
               err = ER_CLI_SYNTAX;
               goto CLEANUP;
               }
            column = (COLUMN_TYPE)tmpU32;

            if (GOOD != (err =
               SymGetLinenumStmt(currentModule,
                                 linenum,
                                 column,
                                 lpAddrRange,      // already a pointer
                                 lpActualLinenum,  // already a pointer
                                 lpActualColumnRange,
                                 &nextIndex)))
               goto CLEANUP;
            *lpSymFoundDesc = currentModule;
            return GOOD;  // done processing linenum
         }
         // fall thru if symbol and not linenum

      case 1: // can be number, function/label, module, or variable  
         // order of lookup:
         // if module and function context, use function context and
         //    look up as local symbol
         //    if no symbol found, look up as module
         // If module context but no function context, use mod context
         //    and look up name
         // if no function nor module context, look up as module
         // if not a module, check for public label or variable

         if (argv[argc-1].isName) { // mod, func, label, or var
            symType = SYM_UNDEFINED;
            if (NULL_SYMBOL != currentModule) { // mod defined
               if (NULL_SYMBOL != currentFunc) {
                  tmpSymbol = currentFunc;       // both defined
               } else {
                  tmpSymbol = currentModule;     // mod defined, func not
               }
               err = SymGetSymbolFromContext(tmpSymbol,
                                             argv[argc-1].argvStrPtr,
                                             &symType,
                                             &foundSymbol,
                                             &isGlobal);
               if (GOOD == err) {
                  *lpSymFoundDesc = foundSymbol;
                  // get module desc; may be different than current context
                  // if symbol found is global and not "inside" current 
                  // context
                  if ( funcFound && ( symType==SYM_FUNCTION ) ) {
                     err = FAILURE;
                     goto CLEANUP;
                  }
                  if (GOOD != (err =
                     SymGetParentModule(*lpSymFoundDesc, &tmpSymbol)))
                     goto CLEANUP;
                  // if user entered module (modFound) and the symbol's module
                  // is not same as current, generate error
                  if ((tmpSymbol != currentModule) && (modFound)) {
                     err = ER_SYM_SYMBOL_NOT_IN_MODULE;
                     goto CLEANUP;
                  } else {
                     currentModule = tmpSymbol;
                  }
               } else if ( funcFound ) {
                     err = FAILURE;
                     goto CLEANUP;
               } else if ((ER_SYMBOL_NOT_FOUND == err) ||
                          (ER_UNKNOWN_LIST_TYPE == err)) {
                  // see if name entry is a local function under the
                  // current module
                  err = SymGetFuncByName(currentModule,
                                         argv[argc-1].argvStrPtr,
                                         &foundSymbol);
                  if (GOOD == err) {
                     *lpSymFoundDesc = foundSymbol;
                  } if ( modFound ) {
                     err = FAILURE;
                     goto CLEANUP;
                  }
                  else if (ER_SYMBOL_NOT_FOUND == err) {
                     // see if #module entry (user overriding current module)
                     err = SymGetModuleDesc(argv[argc-1].argvStrPtr,
                                            (LPSTR)nullStr,
                                            &currentModule);
                     if (GOOD == err) {
                        *lpSymFoundDesc = currentModule;
                     } else {
                        goto CLEANUP;
                     }
                  }
               }
               else {
                  goto CLEANUP;
               }

            } else { // no module or function context; assume input is module
               err = SymGetModuleDesc(argv[argc-1].argvStrPtr,
                                      (LPSTR)nullStr,
                                      &currentModule);
               if (GOOD == err) {
                  UtilOnDemandLoad(currentModule);
                  *lpSymFoundDesc = currentModule;
               } else {
                  if (ER_SYMBOL_NOT_FOUND != err) {
                     goto CLEANUP;
                  } else {
                     // check for public label or var
                     if (GOOD != (err =
                        SymGetSymbolFromContext(NULL_SYMBOL,
                                                argv[argc-1].argvStrPtr,
                                                &symType,
                                                &foundSymbol,
                                                &isGlobal)))
                         goto CLEANUP;
                     *lpSymFoundDesc = foundSymbol;
                  }
               }
            }  // end of else part of if (NULL_SYMBOL != currentModule)
            // now get address of symbol found
            if (GOOD != 
               (err = SymGetSymbolAddress(*lpSymFoundDesc, lpAddrRange)))
               goto CLEANUP;
            //---------------------------------------------------------
            // Check for match between requested address type and found
            // get linenum/column of symbol found
            // BUT only if symbol type is code type
            // if current module is null, must get parent
            //    can find module.
            //---------------------------------------------------------
            {
               ADDR_RANGE_TYPE linenumAddrRange;
               RETCODE    err1;
               BASIC_SYM_ADDR_TYPE symType;

               if (GOOD != (err = SymGetSymbolBasicType(*lpSymFoundDesc,
                                                        &symType)))
                  goto CLEANUP;
               // test input request with result - if don't match
               // return error
               if ((SYM_CODE_ADDR == symType) &&
                   (SYM_DATA_ADDR == requestedAddrType)) {
                  err = ER_SYM_NOT_DATA_ADDRESS;
                  goto CLEANUP;
               }
               if ((SYM_DATA_ADDR == symType) &&
                   (SYM_CODE_ADDR == requestedAddrType)) {
                  err = ER_SYM_NOT_CODE_ADDRESS;
                  goto CLEANUP;
               }
               if (SYM_CODE_ADDR == symType) {
                  // now see if current module valid; if not see if it
                  // is available from found symbol
                  if (currentModule == NULL_SYMBOL) {
                     if (GOOD != (err =
                        SymGetParentModule(*lpSymFoundDesc, &currentModule)))
                        goto CLEANUP;
                     if (currentModule == NULL_SYMBOL) {
                        return GOOD;  // no valid module desc for lpSymFoundD
                     }
                  }
                  err = SymMapAddr2LinenumStmt(lpAddrRange->startAddr,
                                               currentModule,
                                               lpActualLinenum,
                                               lpActualColumnRange,
                                               &linenumAddrRange,
                                               &nextIndex);
                  // NOTES: 09/21/93 - Nghia
                  // If #<token> is the input and <token> is a ASM module name
                  // we don't want to error because there is no line number
                  // loaded for the module.
                  if ((argc == 1) && (err == ER_NO_LINENUMS_ADDED))
                     break; // Break out of switch and fall thru
                  
                  if ((GOOD != err) || (GOOD != err1)) {
                     goto CLEANUP;
                  }
               }
            }
         } else {
            if (SYM_DATA_ADDR == requestedAddrType) {
               // if data requested, can't have line/column inputs
               err = ER_SYM_NOT_DATA_ADDRESS;
               goto CLEANUP;
            }
            // assume a linenum and oonvert; decimal input only
            tmpU32 = strtoul(argv[argc-1].argvStrPtr, &endPtr, 10);
            if ((*endPtr != 0) || (tmpU32 > 0xFFFFL))
               return ER_CLI_SYNTAX;
            linenum = (LINENUM_TYPE)tmpU32;

            if (currentModule == NULL_SYMBOL)
               return ER_SYM_NO_CURRENT_CONTEXT;
            // must have module for line lookup
            if (GOOD != (err =
               SymGetLinenumStmt(currentModule,
                                 linenum,
                                 1,              // assume column 1
                                 lpAddrRange,
                                 lpActualLinenum,  // already a pointer
                                 lpActualColumnRange,
                                 &nextIndex)))
               goto CLEANUP;
            *lpSymFoundDesc = currentModule;
         }
         break;

      default: {
         err = ER_CLI_SYNTAX;
         goto CLEANUP;
         }

   }  // end of switch

   return GOOD;
   // fall thru

CLEANUP:  // only if an error occurred
   return err;
}  // end of SymGetAddrFromName

//#ifdef __cplusplus
//}
//#endif

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


