/****************************************************************************
**
**  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:   S:/tbird/arcppc/symbol/symparse.cpv  $
** 
**    Rev 1.2   12 Mar 1997 14:48:16   hera
** No change.
** 
**    Rev 1.1   03 Mar 1997 15:48:42   hera
** 
**    Rev 1.0   07 Sep 1995 11:16:54   gene
** Initial revision.
** 
**    Rev 1.34   24 Sep 1993 10:34:00   nghia
** Fixed bug in SymGetAddrFromName to avoid reporting error no line number for
** assembly modules.
** 
**    Rev 1.33   05 Jul 1993 08:21:34   doug
** Use general syntax error.
** 
**    Rev 1.32   06 Jan 1993 17:36:36   brucea
** Fixed: didn't handle local (static) function names when entering #mod#func
** 
**    Rev 1.31   04 Nov 1992 07:21:22   brucea
** Fixed: module lookup of modules starting with a digit
** 
**    Rev 1.30   15 Oct 1992 18:40:08   brucea
** Comment additions
** 
**    Rev 1.29   18 Sep 1992 09:45:38   brucea
** Added: check for # as first character in SymGetAddrFromName
** Added: generation of error if parameter count > 3
** 
**    Rev 1.28   14 Sep 1992 17:47:28   brucea
** Fixed bugs in parse routine:
**    wasn't handling error condition for looking up module as first of 2 names
**    if first of two names is function, now handled correctly
**    if only entry is a global function, this now handled correctly
**    if the last name symbol looked up is not in the module entered,
**       an error is generated
** 
**    Rev 1.27   03 Sep 1992 13:12:48   brucea
** Fixed: bug which kept code from finding module when symbol not found in
**    current module context
** 
**    Rev 1.26   29 Aug 1992 22:08:24   brucea
** Fixed problems with SymGetAddrFromName:
**    Destroys addr descriptors if error generated
**    Generates error if requested addr type not equal to that found
**    Fills out linenum/column num when module desc valid
** 
**    Rev 1.25   28 Aug 1992 21:05:40   brucea
** Fixed Free bug: all return <err> after alloc of addrDesc must destroy address
**    before leaving
** 
**    Rev 1.24   25 Aug 1992 20:15:34   brucea
** Added secondary precedence of single entry name to match on module name
**    if local symbol not found
** 
**    Rev 1.23   20 Aug 1992 20:31:02   brucea
** Fixed: addr allocation in SymGetAddrFromName
** 
**    Rev 1.22   13 Aug 1992 11:21:30   brucea
** Remove compiler warnings
** 
**    Rev 1.21   19 Jul 1992 21:52:52   brucea
** Added: if first #entry is not a module, search for public label or var
** 
**    Rev 1.20   10 Jul 1992 18:56:36   brucea
** Removed: IsFuncOrLabel
** Changed: major changes to SymGetAddrFromName to support variable lookup,
**          make modules/functions/linenums work
** 
**    Rev 1.19   11 May 1992 09:50:30   brucea
** Modified: SymGetAddrFromName to support recognition of <mod, funcOrLabel> and
**    <funcOrLabel> (with no mod specified).
** Added: IsFuncOrLabel function
** 
**    Rev 1.18   12 Mar 1992 22:43:26   brucea
** Implemented: SymGetAddrFromName
** 
**    Rev 1.17   04 Mar 1992 18:34:44   brucea
** New file; i.e. old symparse.cpp became symcli.cpp
** 
**    Rev 1.0   04 Mar 1992 13:23:20   brucea
** Initial revision.
**  $Header:   S:/tbird/arcppc/symbol/symparse.cpv   1.2   12 Mar 1997 14:48:16   hera  $
**
**  Copyright (C) 1991 Microtek International.  All rights reserved.
**
*****************************************************************************/

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

#ifndef _ADDR_
#include "addr.h"
#endif

#ifndef __STDLIB_H
#include <stdlib.h>
#endif

#ifndef _STKSERVR_
#include "stkservr.h"
#endif

#ifndef __STRING_H
#include <string.h>
#endif

#ifndef _SYMBLSVR_
#include "symblsvr.h"
#endif

#ifndef _SYMGET_
#include "symget.h"
#endif

#ifndef _SYMERROR_
#include "symerror.h"
#endif

#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     *
                        *                          *
                        ****************************/

   /* 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, 0, 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]
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetAddrFromName(LPSTR                 nameStr,
                   BASIC_SYM_ADDR_TYPE   requestedAddrType, 
                   SYM_DESCRIPTOR    FAR *lpSymFoundDesc,
                   DESCRIPTOR        FAR *lpAddrDesc,
                   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;
   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;

// allocate an address; user must free it when done -
// OR if error generates, must free in CLEANUP below
   if (GOOD != (err = AdrCreateAddress(lpAddrDesc)))
      return err;

   // 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;

   // 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;
            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;
               } 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) {
                  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;
                     // 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,
                                 *lpAddrDesc,      // wants actual desc
                                 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 (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 ((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;
                  } 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) {
                  *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, *lpAddrDesc)))
               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.
            //---------------------------------------------------------
            {
               DESCRIPTOR linenumAddrDesc;
               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
                     }
                  }
                  if (GOOD != (err = AdrCreateAddress(&linenumAddrDesc)))
                     goto CLEANUP;
                  err = SymMapAddr2LinenumStmt(*lpAddrDesc,
                                               currentModule,
                                               lpActualLinenum,
                                               lpActualColumnRange,
                                               linenumAddrDesc,
                                               &nextIndex);
                  err1 = AdrDestroyAddress(linenumAddrDesc);
                  // 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
                                 *lpAddrDesc,      // wants actual desc
                                 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
   AdrDestroyAddress(*lpAddrDesc); // ignore error because another came before
   *lpAddrDesc = 0L;  // Reset output addrDesc
   return err;
}  // end of SymGetAddrFromName

#ifdef __cplusplus
}
#endif

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


