/****************************************************************************
**
**  Name:  symparse.cpp
**
**  Description:
**     This module provides functions which parse an input string and returns
**        the address of the symbol, if found.
**
**  Status:  CODED
**
**  $Log:   S:/tbird/mt2_186/symbol/symparse.cpv  $
** 
**    Rev 1.2   22 Jul 1997 10:10:58   Judy
** 
**    Rev 1.1   26 Feb 1997 11:43:32   Judy
** 
**    Rev 1.1   14 Jan 1997 15:32:12   Judy
** No change.
** 
**    Rev 1.0   14 Jun 1996 16:42:22   Judy
** Initial revision.
** 
**    Rev 1.36   31 Jan 1995 13:57:50   nghia
** Fixed PPR 9896 - AddressOf support #module#StaticVar
** Rewrote the SymGetAddrFromName() routine to use state-machine in parsing
** symbols.  Now support all symbols inputs (3-1 tokens) for various symbol
** types, and handle context lookup correctly.
** Added check for code and data address type only when the caller requested
** address type is known.  Allowed labels to be lookup without module mapping.
** Added check for current context only if there are less than 3 tokens input.
** Validate token syntax correctly - See grammar for more info.
** 
**    Rev 1.35   01 Sep 1994 14:43:00   brucea
** Changed character table to allow ~ (tilde) character as a valid symbol char.
** 
**    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/mt2_186/symbol/symparse.cpv   1.2   22 Jul 1997 10:10:58   Judy  $
**
**  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     *
                        *                          *
                        ****************************/
// Symbol parsing states
// NOTES: START_ONE, START_TWO and START_THREE must match the <argc> value.
typedef enum { DONE = 0, START_ONE, START_TWO, START_THREE,
               CNTXT_ONE, CNTXT_TWO, CNTXT_THREE,
               NO_CNTXT_TWO, NO_CNTXT_ONE, CNTXT_ONE_COL,
               GET_ADDR, GET_LINE
             } PARSER_STATE;

// 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)
//       func       (assume mod context)
//       label      (assume mod context, global context)
//       var        (assume mod context, mod/func context, global context)
//       module
//    2 params
//       num (line), num (col)
//       mod, num (line)
//       mod, func|label
//       mod, var
//       func, label (assume mod context)
//       func, var   (assume mod context)
//       func, line  (assume mod context)
//    3 params
//       mod, num (line), num (col)
//       mod, func, label
//       mod, func, var  
//       mod, func, line
//------------------------------------------------------------------------
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,
		   SYM_TYPE_TYPE     FAR *symType) {

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

   ARG_TYPE       argv[MAX_SUB_SYMBOLS];
   U8             argc;
   LPSTR          locPtr;
   SYM_DESCRIPTOR currentModule = NULL_SYMBOL, currentFunc = NULL_SYMBOL,
                  foundSymbol = NULL_SYMBOL; 
   SYM_DESCRIPTOR tmpSymbol, currentContext;
   //SYM_TYPE_TYPE  symType;
   BASIC_SYM_ADDR_TYPE symAddrType;
   LINENUM_DESCRIPTOR nextIndex;
   DESCRIPTOR     linenumAddrDesc;
   BOOLEAN        isGlobal;
   U32            tmpU32;
   LINENUM_TYPE   linenum = 0;
   COLUMN_TYPE    column = 0;
   S8             *endPtr, nullStr[1];
   PARSER_STATE   parseState;
   RETCODE        err, err1;
         
   // 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

   // Check for valid input parameters
   // cannot have more than 3 tokens
   if (!argc || (argc > 3)) return ER_CLI_SYNTAX;
   
   // 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;

   // Get the current execution context if the input is less than 3 token
   // fill in current module and function.
   if (argc < 3) {
      if ((GOOD != (err = StkGetCurrentModule(&currentModule))) ||
          (GOOD != (err = StkGetCurrentFunction(&currentFunc))))
         goto CLEANUP;
   }

   // PARSING GRAMMAR------------------------------------------:
   // input:
   //      oneToken
   //      | twoToken
   //      | threeToken ;
   //
   // threeToken:
   //      module cntxtTwoToken ;
   //
   // twoToken:
   //      cntxtTwoToken 
   //      | module cntxtOneToken ;
   //
   // cntxtTwoToken:
   //      | linenumber colnumber
   //      | func label
   //      | func var ;
   //
   // oneToken:
   //      cntxtOneToken
   //      | module ;
   //
   // cntxtOneToken: 
   //      | linenumber 
   //      | func
   //      | label
   //      | var ;
   //-----------------------------------------------------------------

   // 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]
   parseState = (PARSER_STATE) argc;
   *symType = SYM_UNDEFINED; //Hera 6/4/97
   while (parseState != DONE)  {     
      // state parsing 
      switch (parseState) {         
         case START_THREE:
         {
            // Valid input are:
            // No context -> #module#line|#col | #module#func#label |
            //               #module#func#var | #module#func#line
            
            // only module valid input
            if (!argv[argc-3].isName) { 
               err = ER_CLI_SYNTAX;
               goto CLEANUP;
            }
            // NOTES: does not handle module path
            if (GOOD != (err = SymGetModuleDesc(argv[argc-3].argvStrPtr,
                                                (LPSTR)nullStr,
                                                &currentModule)))
               goto CLEANUP; // cannot find symbol
            // next state is CNTXT_TWO
            parseState = CNTXT_TWO;
            break;
         } // START_THREE                  

         case START_TWO:
         {
            // Valid input are:
            // Current context -> #line#col | #func#var | #func#label. 
            // No context      -> #module#line | #module#func |
            //                    #module#label | #func#line.
            
            // Transfer parsing state to next state depending upon
            // whether the current module context is valid or not
            parseState = (currentModule != NULL_SYMBOL) ?
                          CNTXT_TWO : NO_CNTXT_TWO;
            break;
         } // START_TWO
         
         case CNTXT_TWO:
         {
            // 2nd token is a symbol: must be a #func, else try no context.
            if (argv[argc-2].isName) {
               // current module context exists, search for child function
               if (GOOD != (err = SymGetSymbolChild(currentModule, symType,
                                                    &currentContext)))
                  goto CLEANUP;
               // current module context does not have child function,
               // go back to NO_CNTXT_TWO to try it as a module.   
               if (*symType == NULL_SYMBOL) {
                  parseState = NO_CNTXT_TWO;
                  break;
               }
               // Search for function
               *symType = SYM_FUNCTION;
               if ((err = SymGetSymbolFromContext(currentContext,
                                             argv[argc-2].argvStrPtr,
                                             symType,  // input and output
                                             &foundSymbol,
                                             &isGlobal)) != GOOD) {
                  // Exit if error is other than ER_SYMBOL_NOT_FOUND
                  if (err != ER_SYMBOL_NOT_FOUND)
                     goto CLEANUP;
                  // 2nd token is not found with module context,
                  // go back to NO_CNTXT_TWO to try it as a module.
                  parseState = NO_CNTXT_TWO;
                  break;
               }
               // if not found function then 
               if (SYM_FUNCTION != *symType) {
                  err = ER_SYMBOL_NOT_FOUND;
                  goto CLEANUP;
               }
               // Continue parsing, save the found function
               currentFunc = foundSymbol;               
               // transfer parseState to CNTXT_ONE
               // to continue parsing the next token
               parseState = CNTXT_ONE;
            } else {
               // token must be a number: #line
               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;
               }
               // Convert linenum; decimal input only
               tmpU32 = strtoul(argv[argc-2].argvStrPtr, &endPtr, 10);
               if ((*endPtr != 0) || (tmpU32 > 0xFFFFL)) {
                  err = ER_CLI_SYNTAX;
                  goto CLEANUP;
               }
               // save the line number and continue to next token
               linenum = (LINENUM_TYPE)tmpU32;
               // continue to look: #<line>#col
               parseState = CNTXT_ONE_COL;
            }
            break;
         } // CNTXT_TWO
         
         case NO_CNTXT_TWO:
         {
            // no current module context; symbol can be a module
            // or global function followed by symbol
            if (!argv[argc-2].isName) {
               err = ER_CLI_SYNTAX;
               goto CLEANUP;
            }          
            // NOTES: needs fixup to include search extraction of module path
            if ((err = SymGetModuleDesc(argv[argc-2].argvStrPtr,
                                         (LPSTR)nullStr,
                                         &currentModule)) != GOOD) {
               if (ER_SYMBOL_NOT_FOUND != err)
                  goto CLEANUP;

               // Check it for global function
               *symType = SYM_UNDEFINED;
               if ((err = SymGetSymbolFromContext(NULL_SYMBOL,
                     argv[argc-2].argvStrPtr, symType, &foundSymbol,
                     &isGlobal)) != GOOD)
                  goto CLEANUP;
               
               if (SYM_FUNCTION == *symType) {
                  currentFunc = foundSymbol;
                  // fill in current module
                  if (GOOD != (err =  SymGetParentModule(currentFunc,
                     &tmpSymbol))) goto CLEANUP;
                  currentModule = tmpSymbol;
               } 
            }
            // goto next state CNTXT_ONE
            parseState = CNTXT_ONE;
            break;
         } // NO_CNTXT_TWO

         case START_ONE:
         {
            // Valid input are:
            // Current context -> #line | #func | #label | #var [| #col].
            // No context      -> #module. 

            // Always look up for context first
            // Transfer parsing state to next state depending upon
            // whether the current module context is valid or not
            parseState = (currentModule != NULL_SYMBOL) ?
                          CNTXT_ONE : NO_CNTXT_ONE;
            break;            
         } // START_ONE

         case CNTXT_ONE_COL:
         {
            // Valid only if the linenum token is valid
            if (!linenum || !currentModule) {
               // NOTES: actualy this should be an algorithm logic error.
               err = ER_SYM_NO_CURRENT_CONTEXT;
               goto CLEANUP;
            }
            // Parse token as #col.
            // Convert input to column value; 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;
            parseState = GET_LINE;
            break;
         } // CNTXT_ONE_COL
         
         case CNTXT_ONE:
         {
            if (argv[argc-1].isName) {
               // token is one of these (func, label, var).
               *symType = SYM_UNDEFINED;               
               // use the closest symbol context
               tmpSymbol = (NULL_SYMBOL != currentFunc) ? currentFunc :
                           currentModule;
               // lookup symbol in the current context
               if ((err = SymGetSymbolFromContext(tmpSymbol,
                                                  argv[argc-1].argvStrPtr,
                                                  symType,
                                                  &foundSymbol,
                                                  &isGlobal)) != GOOD) {
                  if ((err != ER_SYMBOL_NOT_FOUND) &&
                      (err != ER_UNKNOWN_LIST_TYPE)) {
                     // Retry the token as #module symbol
                     parseState = NO_CNTXT_ONE;
                     break;
                  }
                  // Check for token is a local function under
                  // the current module
                  if ((err = SymGetFuncByName(currentModule,
                                              argv[argc-1].argvStrPtr,
                                              &foundSymbol)) != GOOD) { 
                     if (err != ER_SYMBOL_NOT_FOUND) {
                        goto CLEANUP;
                     }
                     // Retry the input token as #module
                     parseState = NO_CNTXT_ONE;
                     break;
                  }
                  // Saved the found symbol for address lookup
                  *lpSymFoundDesc = foundSymbol;                  
                  // Next state is GET_ADDR
                  parseState = GET_ADDR;
                  break;
               }
               
               // Save the found symbol to lookup address
               *lpSymFoundDesc = foundSymbol;
               // if found symbol is not a global, check for
               // matching current module
               if (!isGlobal) {
                  // 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 and
                  // the symbol's module is not same as current,
                  // generate error
                  if ((tmpSymbol != currentModule) &&
                      (currentModule != NULL_SYMBOL)) {
                     err = ER_SYM_SYMBOL_NOT_IN_MODULE;
                     goto CLEANUP;
                  }
                  // Save the module for address lookup
                  currentModule = tmpSymbol;
                  // Next state is GET_ADDR
               }
               // Goto next state
               parseState = GET_ADDR;
            } else {
               // token must be a line number
               if (SYM_DATA_ADDR == requestedAddrType) {
                  // if data requested, can't have line inputs
                  err = ER_SYM_NOT_DATA_ADDRESS;
                  goto CLEANUP;
               }
               // convert to linenum; decimal input only
               tmpU32 = strtoul(argv[argc-1].argvStrPtr, &endPtr, 10);
               if ((*endPtr != 0) || (tmpU32 > 0xFFFFL)) {
                  err = ER_CLI_SYNTAX;
                  goto CLEANUP;
               }
               linenum = (LINENUM_TYPE)tmpU32;
               column  = 1; // assume value
               // Goto next state 
               parseState = GET_LINE;              
            }
            break;
         } // CNTXT_ONE

         case NO_CNTXT_ONE :
         {
            // see if #module entry (user overriding current module)
            if ((err = SymGetModuleDesc(argv[argc-1].argvStrPtr,
                       (LPSTR)nullStr, &currentModule)) != GOOD) {
               if (ER_SYMBOL_NOT_FOUND != err)  goto CLEANUP;
               // check if token is a public label or variable
               if (GOOD != (err = SymGetSymbolFromContext(NULL_SYMBOL,
                     argv[argc-1].argvStrPtr, symType,
                     &foundSymbol,&isGlobal))) {
                  goto CLEANUP;
               }
               *lpSymFoundDesc = foundSymbol;
	    } else {
	       if (*symType == SYM_UNDEFINED)
		  *symType = SYM_MODULE; //Hera 6/4/97
               *lpSymFoundDesc = currentModule;
            }
            parseState = GET_ADDR;
            break;
         } // NO_CNTXT_ONE

         case GET_ADDR:
         {
            if ((*lpSymFoundDesc) == NULL_SYMBOL) {
               err = ER_SYMBOL_NOT_FOUND;
               goto CLEANUP;
            }
            // 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.
            //---------------------------------------------------------
            if (GOOD != (err = SymGetSymbolBasicType(*lpSymFoundDesc,
                  &symAddrType)))  goto CLEANUP;
            
            // Do need to check for matching address type
            // since the requested is unknown.
            if (requestedAddrType != SYM_UNKNOWN_ADDR) {            
               // test input request with result - if don't match
               // return error
               if ((SYM_CODE_ADDR == symAddrType) &&
                   (SYM_DATA_ADDR == requestedAddrType)) {
                  err = ER_SYM_NOT_DATA_ADDRESS;
                  goto CLEANUP;
               }
               if ((SYM_DATA_ADDR == symAddrType) &&
                   (SYM_CODE_ADDR == requestedAddrType)) {
                  err = ER_SYM_NOT_CODE_ADDRESS;
                  goto CLEANUP;
               }
            }
            if (SYM_CODE_ADDR == symAddrType) {
               // 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,
                                            currentModule, //Hera 6/4/97
                                            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)) {
                  parseState = DONE;
                  break;
               }
               if ((GOOD != err) || (GOOD != err1)) {
                  // if err == ER_ADDRESS_NOT_FOUND and
                  // symbol is a SYM_LABEL, SYM_PUBLIC_LABEL
                  // return the found address any way
                  // NOTES: both multi-segment label
                  // and public label do not need to map to module.
                  if ((err != ER_ADDRESS_NOT_FOUND) ||
                      ((*symType != SYM_LABEL) &&
		       (*symType != SYM_PUBLIC_LABEL))) {
                     err = ER_SYMBOL_NOT_FOUND;
                     goto CLEANUP;
                  }
               } 
            }
            parseState = DONE;
            break;
         } // GET_ADDR
         
         case GET_LINE:
         {   
            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;
            }
            // Now lookup #module#line#col address 
            if (GOOD != (err =
               SymGetLinenumStmt(currentModule,
                                 linenum,
                                 column,  
                                 *lpAddrDesc,      // wants actual desc
				 lpActualLinenum,  // already a pointer
                                 lpActualColumnRange,
                                 &nextIndex)))
               goto CLEANUP;
            // set the return module
            *lpSymFoundDesc = currentModule;
            parseState = DONE;
            break;
         }

         default:
         {
            err = ER_CLI_SYNTAX;
            goto CLEANUP;
         }
         
     } // end of switch
   } // while

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


