/****************************************************************************
**
**  Name:  symcli.cpp
**
**  Description:
**     This module parses the info coming from the cli server and then 
**     dispatches the appropriate modules.
**
**  Status:  PRELIMINARY
**
**  $Log:   S:/tbird/mt2_68k/symbol/symcli.cpv  $
** 
**    Rev 1.0   13 Feb 1997 09:06:22   gene
** Initial revision.
** 
**    Rev 1.0   07 Sep 1995 11:16:48   gene
** Initial revision.
** 
**    Rev 1.26   25 Feb 1994 12:46:44   nghia
** Added output count for each symbol types in _count shell command.
** 
**    Rev 1.25   30 Sep 1993 15:04:40   nghia
** Revised _displayTypes to include "built-in|user" options.
** Added displaysymbols _varlife to dump out all symbol + var lifetime info.
** 
**    Rev 1.24   24 Sep 1993 10:36:00   nghia
** Added SymCheckForNoSymbols() to all symbol shell command to report the error
** appropriately.  In the past it reported "Index invalid....@#$!".
** Added DisplayTypes() to dump out the type table for debug and testing purpose
** 
**    Rev 1.23   05 Jul 1993 08:20:56   doug
** Use general syntax error.
** 
**    Rev 1.22   25 Jun 1993 17:54:00   paul
** make abortFromEsc a BOOLEAN.
** 
**    Rev 1.21   25 Jun 1993 17:34:44   paul
** Change CHECK_ABORT to TskCheckAbort
** 
**    Rev 1.20   23 Mar 1993 17:10:38   courtney
** Symbol pointers are huge in declaration and cast.
** 
**    Rev 1.19   08 Dec 1992 06:56:58   brucea
** Changed (!funcDesc) type tests to (NULL_SYMBOL != ...)
** 
**    Rev 1.18   03 Dec 1992 07:35:12   brucea
** Added: DisplayModule to provide means of displaying symbols for a single
**    module
** 
**    Rev 1.17   15 Oct 1992 18:41:22   brucea
** Added: pc to command parameter of nameof
** 
**    Rev 1.16   15 Sep 1992 11:43:44   brucea
** Changed: AdrConvAddressToTextNoFill to ..WithParams
** 
**    Rev 1.15   14 Sep 1992 17:49:34   brucea
** Only generate EVENT_SYMBOL_DELETED when the command is given from the CLI,
**    not when a reload is done via loader.
** Changed: AdrConvAddressToText call to ...WithParams
** 
**    Rev 1.14   08 Sep 1992 13:05:46   brucea
** Added: events.h include
** 
**    Rev 1.13   29 Aug 1992 22:09:38   brucea
** Addressof: call to SymGetAddrFromName now does not destroy addr desc when
**    error returned
** 
**    Rev 1.12   20 Aug 1992 20:29:46   brucea
** Removed: create address prior to call to SymGetAddrFromName since it is 
**    alloc'ed in this function
** 
**    Rev 1.11   13 Aug 1992 19:26:18   brucea
** Comment change
** 
**    Rev 1.10   13 Aug 1992 11:20:54   brucea
** Removed compiler warnings
** Changed: wsprintf to sprintf
** 
**    Rev 1.9   08 Aug 1992 10:50:18   tom
** New CLI registration changes.
** 
**    Rev 1.8   25 Jul 1992 16:45:26   brucea
** Moved: InitCServer, SendMessageToCli into this module; removed second
**    parameter from SendMessageToCli
** Added: (U16) casts to all argv parameters
** 
** 
**    Rev 1.7   19 Jul 1992 21:15:16   brucea
** Added to count output for alloc'ed size of each table
** Implemented: NameOf
** 
** 
**    Rev 1.6   10 Jul 1992 18:54:14   brucea
** Removed: SymLoad
** Added: Count, NameOf, and AddressOf, 
** 
**    Rev 1.5   24 Jun 1992 18:05:34   brucea
** Removed: #include "load.h"
** Changed: SymLoad to #if 0 out the load command.  At a future time, this
**    code should be removed entirely.
** 
**    Rev 1.4   15 Jun 1992 09:29:30   brucea
** Fixed up: compiler warnings about no returns
** 
**    Rev 1.3   30 Apr 1992 17:42:20   brucea
** Changed: made internal functions PRIVATE
** Fixed: DisplayFunctions, DisplayModules to handle nested blocks
** Cleaned up: PrintBase code
** 
**    Rev 1.2   20 Apr 1992 08:43:14   brucea
** Added: DisplayLines
** Changed: internal CLI commands use _ in front of command name
** 
**    Rev 1.1   01 Apr 1992 23:37:36   brucea
** Fixed: compiler warnings
** 
**    Rev 1.0   04 Mar 1992 13:23:20   brucea
** Initial revision.
** 
**    Rev 1.16   27 Feb 1992 22:50:00   brucea
** Removed: definition of CheckAbort [moved to BASETYP.H]
** Renamed: CheckAbort() to CHECK_ABORT()
** 
**    Rev 1.15   14 Feb 1992 15:03:12   brucea
** Added CheckAbort() macro and a call to it to initialize the aborting of the
**    symbol display with the ESC key
** Changed strtol to strtoul (for unsigned addresses) and check for invalid
**    parsing of the address string
** 
**    Rev 1.14   04 Feb 1992 16:25:18   brucea
** Added #pragma argsused to RemoveSymbols function
** 
**    Rev 1.13   22 Jan 1992 14:56:40   brucea
** Changed display code so that module mode does not display variables.
** In GetBase, now only check for (argc > 2)
** Added parameter count check in RemoveSymbols
** Changed param count check from 4 to 3 in SetBase
** Changed last param in strtol call to 0 from 16 to allow octal, decimal, or
**   hex input as valid.
** 
**    Rev 1.12   10 Jan 1992 19:23:36   brucea
** Added event notification when modifying a base address
** Fixed bug to indent module variables when they are printed
** 
**    Rev 1.11   08 Jan 1992 11:30:36   john
** Created new commands:  GetBase and SetBase.
** 
**    Rev 1.10   06 Jan 1992 16:05:36   john
** Added code to walk the symbol table to print out all symbols
** according to their precedence.
** 
**    Rev 1.9   20 Dec 1991 16:51:32   john
** Changed code for display of symbols.  Not complete
** 
**    Rev 1.8   11 Dec 1991 16:31:44   john
** Started adding new commands.  These are incomplete.
** 
**    Rev 1.7   10 Dec 1991 11:55:46   john
** Added header to get prototype for symremovesymbols
** 
**    Rev 1.6   09 Dec 1991 14:44:46   john
** Added code to remove symbols.
** Added code to print all symbols in a user/debug format.
** Renamed SymPrintSymbols to PrintSymbols to make the CLI interface
** better.
** 
**    Rev 1.5   06 Nov 1991 13:30:12   john
** Removed compile time warnings w/max warning level enabled
** 
**    Rev 1.4   11 Oct 1991 15:39:58   john
** Modified if statements to return err as the primary action.
** 
**    Rev 1.3   03 Oct 1991 11:45:56   john
** Made changes to allow much longer path names, and fixed the code
** that tried to make all the cli parsed text lower case.
** 
**    Rev 1.2   27 Sep 1991 17:56:54   brucea
** Added #include "load.h"
** 
**    Rev 1.1   27 Sep 1991 17:45:12   john
** Added call to ascii file loader.
** 
**    Rev 1.0   25 Sep 1991 13:35:12   john
** Initial revision.
**
**  $Header:   S:/tbird/mt2_68k/symbol/symcli.cpv   1.0   13 Feb 1997 09:06:22   gene  $
**
**  Copyright (C) 1991 Microtek International.  All rights reserved.
**
*****************************************************************************/

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

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

#ifndef _BASETBL_
#include "basetbl.h"
#endif

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

#ifndef _BYADRTBL_
#include "byadrtbl.h"
#endif

#ifndef _CLISRV_
#include "clisrv.h"
#endif

#ifndef _CPU_
#include "cpu.h"
#endif

#ifndef __CTYPE_H
#include <ctype.h>
#endif

#ifndef _ENLIB_
#include "enlib.h"
#endif

#ifndef _EVENTS_
#include "events.h"
#endif

#ifndef _HASHTBL_
#include "hashtbl.h"
#endif

#ifndef _LINENUM_
#include "linenum.h"
#endif

#ifndef _MEMPOOL_
#include "mempool.h"
#endif

#ifndef _ORDTBL_
#include "ordtbl.h"
#endif

#ifndef _PVTASK_
#include "pvtask.h"
#endif

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

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

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

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

#ifndef _SYMCLI_
#include "symcli.h"
#endif

#ifndef _SYMCLIPR_
#include "symclipr.h"
#endif

#ifndef _SYMUTIL_
#include "symutil.h"
#endif

#ifndef _TYPETBL_
#include "typetbl.h"
#endif

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

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

extern TABLE_OFFSET rootOffset;

extern BaseIndexTable bit;
extern MemPool st;
extern OrdinalTable ot;
extern HashTable ht;
extern TypeTable tt;
extern LinenumTable lt;
extern IndexTable typit;

//----------------------------------------------------------------------------
// Report each symbol type
extern U32 symbolStat[];
extern const S8 *symTypeName[];
extern U8 symTypeMap[];

                       /****************************
                        *                          *
                        *     LOCAL VARIABLES      *
                        *                          *
                        ****************************/

PRIVATE HANDLE cliServerHandle;

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

RETCODE PRIVATE DisplayModules(SYM_DISPLAY_TYPE displayType);
RETCODE PRIVATE DisplayFunctions(SYM_DESCRIPTOR moduleDescriptor, 
                         SYM_DISPLAY_TYPE displayType, U16 indentLevel);
RETCODE PRIVATE DisplayVariables(SYM_DESCRIPTOR symbolDescriptor, 
                         SYM_DISPLAY_TYPE displayType, U16 indentLevel);
RETCODE PRIVATE PrintBase(TABLE_OFFSET baseOffset);

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/

#define NESTING_LEVEL  1

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


//--------------------------------------------------------------------------
// InitCServer
//
// Description:
//
// Input Parameters:
//
// Output Parameters:
//
// Errors:
//
//--------------------------------------------------------------------------
RETCODE EXPORT InitCServer(HANDLE cliHandle, HANDLE dllHandle) {

   CSERVER_NEW_REGISTRATION FAR * msgBufPtr;

   cliServerHandle = cliHandle;
   msgBufPtr =
      (CSERVER_NEW_REGISTRATION FAR *)TMalloc(sizeof(CSERVER_VARIABLE_VALUE));
   if (msgBufPtr == NULL) {
      return(ER_OUT_OF_MEMORY);
   }

   msgBufPtr->stringResourceHandle = dllHandle;

   msgBufPtr->serverNameIndex = 30;
   msgBufPtr->dllNameIndex = 31;
   msgBufPtr->numberOfCommandsIndex = 32;
   msgBufPtr->commandStartIndex = 33;
   SendMessage(cliHandle, CLI_NEW_SVR_REGISTRATION, CLI_NEW_SVR_REGISTRATION,
      (DWORD)msgBufPtr);
   return GOOD;
}  // end of InitCServer


//--------------------------------------------------------------------------
// SendMessageToCli
//--------------------------------------------------------------------------
RETCODE SendMessageToCli(U8 FAR *msgPtr) {

   HANDLE msgBufHandle;
   LPSTR cpPtr;
   U16 i;
   U16 size;

   size = strlen((CHAR *)msgPtr);
   // allocate enough memory for the string passed in and the size ;
   // of the CSERVER_RESULTS packet, and then subtract out the extra ;
   // space allocated to the messageText field. ;
   if ((msgBufHandle =
      GlobalAlloc(GMEM_MOVEABLE,
                  (size + sizeof(CSERVER_RESULTS) + 2))) == (HANDLE)NULL) {
      return(ER_OUT_OF_MEMORY);
   } else if ((cpPtr = GlobalLock(msgBufHandle)) == NULL) {
      GlobalFree(msgBufHandle);
      return(ER_WINDOWS_MEMLOCK);
   }

   ((CSERVER_RESULTS *)cpPtr)->target = 0;  // address to the cli server;
   ((CSERVER_RESULTS *)cpPtr)->variantCode = CLI_SERVER_RESULTS;
   ((CSERVER_RESULTS *)cpPtr)->resultTextLength = size; // size of message;
   // add sizeof CSERVER_RESULTS header to cpPtr and then fill in the ;
   // message to be sent to the cli server ;
   cpPtr = ((CSERVER_RESULTS *)cpPtr)->messageText;
   /* copy the data from msgPtr to the global memory space */
   for(i = 0; i < size; i++) {
      *cpPtr++ = *msgPtr++;
   }
   /*TRUE on success*/
   if (!(SendMessage(cliServerHandle, CLI_SERVER_RESULTS, msgBufHandle,
                     (CLI_SERVER_RESULTS | 0x0L)))) {
      ErrDisplayError(ER_SYM_SEND_MESSAGE, FORCE_POPUP);
   }
   return GOOD;
}  // end of SendMessageToCli


//--------------------------------------------------------------------------
// DisplayFunctions
//
// Parameters:
//    symbolDescriptor: desc to function to be displayed
//    displayType: indicates what level of printing is to be done
//    indentLevel: what level of indent nesting for this call
//--------------------------------------------------------------------------
RETCODE PRIVATE DisplayFunctions(SYM_DESCRIPTOR   funcDesc,
                                 SYM_DISPLAY_TYPE displayType,
                                 U16              indentLevel) {
   RETCODE        err;
   SYM_DESCRIPTOR newFuncDesc;
   SYM_TYPE_TYPE  symType;
   
   if (NULL_SYMBOL != funcDesc) {
      if ((err = SymPrintSymbol(funcDesc,
                                displayType, 
                                indentLevel)) != GOOD)
         return err;
      if ((err = DisplayVariables(funcDesc,
                                  displayType, 
                                  indentLevel + NESTING_LEVEL)) != GOOD)
         return err;
      
      // recurse down child pointer
      if ((err = SymGetSymbolChild(funcDesc, &symType, &newFuncDesc))
         != GOOD)
         return err;

      if((err = DisplayFunctions(newFuncDesc, displayType, 
                                 indentLevel + NESTING_LEVEL)) != GOOD)
         return err;

      // recurse down sibling pointer
      if ((err = SymGetSymbolSibling(funcDesc, &symType,
                                     &newFuncDesc)) != GOOD)
         return err;
      if((err = DisplayFunctions(newFuncDesc, displayType, 
                                 indentLevel)) != GOOD)
         return err;
   }
   return GOOD;
}

   
//--------------------------------------------------------------------------
// DisplayModules
//
// Description: Sends module information to the CLI for the user.
//
// Input Parameters:
//    moduleDesc: descriptor of module to display
//    displayType: indicates how much to display
//--------------------------------------------------------------------------
RETCODE PRIVATE
DisplayModule(SYM_DESCRIPTOR   moduleDesc,
              SYM_DISPLAY_TYPE displayType) {

   RETCODE        err;
   SYM_DESCRIPTOR funcDesc;
   SYM_TYPE_TYPE  symType;
   BOOLEAN abortFromEsc = FALSE;
   

   // print out the module info;
   if (GOOD != (err = SymPrintSymbol(moduleDesc, displayType, 0)))
         return err;

   if (displayType != MODULE_DISPLAY) {
      // display all module global vars
      if (GOOD != (err = DisplayVariables(moduleDesc,
                                          displayType,
                                          NESTING_LEVEL)))
         return err;
   }
   // escape when key pressed
   if ((TskCheckAbort(&abortFromEsc) != GOOD) || abortFromEsc) 
      return ER_ABORT_FROM_ESC;     

   // loop through the functions
   if ((err = SymGetSymbolChild(moduleDesc, &symType,
                                &funcDesc)) != GOOD)
      return err;
   if ((NULL_SYMBOL != funcDesc) && (displayType != MODULE_DISPLAY)) {
      if((err = DisplayFunctions(funcDesc,
                                 displayType,
                                 NESTING_LEVEL)) != GOOD)
      return err;
   }
   return GOOD;
}  // end of DisplayModule


//--------------------------------------------------------------------------
// DisplayModules
//
// Description: Sends module symbol information to the CLI for the user.
//
// Input Parameters:
//    displayType: how much of symbol hierarchy to display
//--------------------------------------------------------------------------
RETCODE PRIVATE DisplayModules(SYM_DISPLAY_TYPE displayType) {

   RETCODE        err;
   SYM_DESCRIPTOR moduleDesc;
   SYM_TYPE_TYPE  symType;
   
   // display all module names ;
   if ((err = SymGetModuleListHead(&moduleDesc)) != GOOD) 
      return err;
   
   while (moduleDesc) {
      if (GOOD != (err = DisplayModule(moduleDesc, displayType)))
         return err;
      
      if ((err = SymGetSymbolSibling(moduleDesc, &symType,
         &moduleDesc)) != GOOD)
         return err;
   }
   return GOOD;
}  // end of DisplayModules


//--------------------------------------------------------------------------
// DisplayVariables
//--------------------------------------------------------------------------
RETCODE PRIVATE
DisplayVariables(SYM_DESCRIPTOR   symbolDescriptor, 
                 SYM_DISPLAY_TYPE displayType,
                 U16              indentLevel) {

   RETCODE        err;
   SYM_DESCRIPTOR currentVarDesc;
   SYM_TYPE_TYPE  symType;
   U16            listCount, i;
   
   // make sure we are supposed to display variables. ;
   if (displayType == FUNCTION_DISPLAY)
      return GOOD;
   
   for (i=0; i<4; i++) {
      
      // cycle through all of the possible lists. ;
      switch (i) {
         case 0: 
            symType = SYM_LOCAL_VAR;
            break;
         case 1:
            symType = SYM_LABEL;
            break;
         case 2:
            symType = SYM_CONSTANT;
            break;
         case 3:
            symType = SYM_MISCELLANEOUS;
            break;
      }
      if ((err = SymGetSymbolHeadList(symbolDescriptor, symType,
                                      &currentVarDesc, &listCount)) != GOOD) 
         return err;
      
      // print out all elements of the list. ;
      while (listCount--) {
         if ((err = SymPrintSymbol(currentVarDesc, displayType, indentLevel))
            != GOOD)
            return err;
         if ((err = SymGetSymbolSibling(currentVarDesc, &symType, 
                                       &currentVarDesc)) != GOOD)
            return err;
      }
   }
   return GOOD;
}

   
//--------------------------------------------------------------------------
// DisplayLines
//--------------------------------------------------------------------------
RETCODE PRIVATE DisplayLines(SYM_DISPLAY_TYPE displayType) {

   RETCODE        err;
   SYM_DESCRIPTOR moduleDesc, tmpDesc;
   SYM_TYPE_TYPE  symType;
   U16            indentLevel = 0;

   // get start of module linked list
   if ((err = SymGetModuleListHead(&moduleDesc)) != GOOD) 
      return err;
   
   do {
      // print out the module info;
      if((err = SymPrintSymbol(moduleDesc, displayType, indentLevel)) 
         != GOOD) return err;

      // display all linenums if they have been loaded; check first
      if (GOOD != (err = SymPrintLines(moduleDesc)))
         return err;

      // get next module
      if ((err = SymGetSymbolSibling(moduleDesc, &symType,
         &tmpDesc)) != GOOD) return err;
      moduleDesc = tmpDesc;
   } while(moduleDesc);

   return GOOD;
}  // end of DisplayLines


//--------------------------------------------------------------------------
// DisplaySymbols
//--------------------------------------------------------------------------
RETCODE EXPORT
DisplaySymbols(LPSTR cmdString, U32 argc, U32 argv[]) {

   RETCODE          err;
   S8               command[(MAX_SYMNAME_LENGTH/4)];
   SYM_DISPLAY_TYPE displayType;
   BOOLEAN          noSymbolsLoaded;
   LPSTR            cmdPtr;
   S8               nullStr[1];
   SYM_DESCRIPTOR   moduleDesc;
   BOOLEAN          abortFromEsc;
   

   // this is checking for the basic debug PRINT command
   if( (argc > 2) || (argc < 1)) {
      return(ER_CLI_SYNTAX);
   }
   if (GOOD != (err = SymCheckForNoSymbols(&noSymbolsLoaded)))
      return err;
   if (noSymbolsLoaded) {
      return ER_SYMBOL_NOT_A_MODULE;  // reports "no symbols are loaded"
   }
   // clears keyboard of any residual escape key hit
   err = TskCheckAbort(&abortFromEsc);
   if(err!=GOOD) return err;

   if (argc == 2) {
      cmdPtr = &cmdString[(U16)argv[1]];
      if (*cmdPtr == '#') {
         cmdPtr++;
         nullStr[0] = '\0';
         if (GOOD != (err = SymGetModuleDesc(cmdPtr,
                                             (LPSTR)nullStr,
                                             &moduleDesc)))
            return err;
         return DisplayModule(moduleDesc, PUBLIC_DISPLAY);
      }
      strcpy(command, &cmdString[(U16)argv[1]]);
      strlwr(command);
      if (!strcmp(command, "_debug")) {
         displayType = DEBUG_DISPLAY;
         if((err = SymPrintSymbols(displayType)) != GOOD)
            return err;
         if((err = SymPrintBases(displayType)) != GOOD)
            return err;

      } else if (!strcmp(command, "modules")) {
         displayType = MODULE_DISPLAY;
         // print out all modules ;
         if((err = DisplayModules(displayType)) != GOOD)
            return err;

      } else if (!strcmp(command, "functions")) {
         displayType = FUNCTION_DISPLAY;
         // print out all modules, and all functions;
         if((err = DisplayModules(displayType)) != GOOD)
            return err;

      } else if (!strcmp(command, "publics")) {
         displayType = PUBLIC_DISPLAY;
         // print out everything including publics ;
         err = DisplayModules(displayType);
         if ((GOOD != err) && (ER_SYM_NO_MODULES_LOADED != err))
            return err;
         // print out all of the public variables
         if((err = DisplayVariables(rootOffset, displayType, 0)) != GOOD)
            return err;

      } else if (!strcmp(command, "lines")) {
         displayType = LINES_DISPLAY;
         // print out module and all lines in module ;
         if((err = DisplayLines(displayType)) != GOOD)
            return err;

      } else if (!strcmp(command, "_varlife")) {
        displayType = VARLIFE_DISPLAY;
        // print out module and all var with it life-time information
        if ((err = DisplayModules(displayType)) != GOOD)
           return err;
      } else {
         return(ER_CLI_SYNTAX);
      }
   } else {
      // default is to display all symbols except publics
      displayType = USER_DISPLAY;
      if (GOOD != (err = DisplayModules(PUBLIC_DISPLAY)))
         return err;
   }
   return GOOD;
}


//--------------------------------------------------------------------------
// PrintBase
//--------------------------------------------------------------------------
RETCODE PRIVATE PrintBase(TABLE_OFFSET baseOffset) {

   RETCODE      err, firstErr = GOOD;
   S8           tempStr[MAX_SYMNAME_LENGTH];
   BaseSymbol   HUGE *basePtr;
   U16          len, column;
   DESCRIPTOR   tmpAddr;

#define SYMCLI_MAX_COLUMN (20)

   if (GOOD != (err = AdrCreateAddress(&tmpAddr)))
      return err;

   basePtr = (BaseSymbol HUGE *)st.GetHugeDataPtr(baseOffset);
   st.GetString(basePtr->GetBaseNameOffset(), (U8 FAR *)tempStr);
   len = strlen(tempStr);
   if (SYMCLI_MAX_COLUMN <= len) {
      column = len + 1;
   } else {
     column = SYMCLI_MAX_COLUMN;
   }
   for (; len < column; tempStr[len++] = ' ');
   if (GOOD != (err = AdrSetAddrOffset(tmpAddr,
                                       basePtr->GetBaseAddress())))
      goto CLEANUP;
   if (GOOD != (err = AdrConvAddressToTextWithParams(tmpAddr, TRUE, FALSE,
                                                     (LPSTR)&tempStr[len])))
      goto CLEANUP;
   if (GOOD != 
      (err = SendMessageToCli((U8 FAR *) tempStr)))
      goto CLEANUP;

CLEANUP:
   firstErr = err;
   err = AdrDestroyAddress(tmpAddr);
   if (!firstErr) firstErr = err;
   return firstErr;
}  // end of PrintBase


//--------------------------------------------------------------------------
// GetBase
//--------------------------------------------------------------------------
RETCODE EXPORT
GetBase(LPSTR cmdString, U32 argc, U32 argv[]) {

   RETCODE      err;
   S8           baseName[MAX_SYMNAME_LENGTH];
   TABLE_OFFSET rootBaseOffset, baseOffset;
   BaseSymbol   HUGE *basePtr;
   BOOLEAN      noSymbolsLoaded;

   if (argc > 2) return(ER_CLI_SYNTAX);
   if (GOOD != (err = SymCheckForNoSymbols(&noSymbolsLoaded)))
      return err;
   if (noSymbolsLoaded) {
      return ER_SYMBOL_NOT_A_MODULE;  // reports "no symbols are loaded"
   }
   
   if (argc == 2) {
      // get the base symbol that matches the name given by the user ;
      strcpy(baseName, &(cmdString[(U16)argv[1]]));
      if (GOOD != (err = GetBaseOffsetByName((LPSTR)baseName, baseOffset)))
         return err;
      if (GOOD != (err = PrintBase(baseOffset)))
         return err;
   } else {
      // print all bases
      // get the offset of the first base symbol ;
      baseOffset = rootBaseOffset = bit.GetRootBaseSymbolOffset();
      if (!rootBaseOffset) return(ER_INDEX_NOT_IN_TABLE);
      do {
         if (GOOD != (err = PrintBase(baseOffset)))
            return err;
         basePtr = (BaseSymbol HUGE *)st.GetHugeDataPtr(baseOffset);
         baseOffset = basePtr->GetNextBaseOffset();
      } while (baseOffset);
   }
   return GOOD;
}  // end of GetBase


//--------------------------------------------------------------------------
// RemoveSymbols
//--------------------------------------------------------------------------
#pragma argsused
RETCODE EXPORT
RemoveSymbols(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err;

   if (argc > 1)
      return(ER_CLI_SYNTAX);
   if ((err = SymRemoveSymbols()) != GOOD)
      return err;
   // notify interested parties
   return EnlEventNotify(EVENT_SYMBOL_DELETED);
}


//--------------------------------------------------------------------------
// SetBase
//--------------------------------------------------------------------------
RETCODE EXPORT
SetBase(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE         err;
   TABLE_OFFSET    baseOffset;
   BaseSymbol HUGE *basePtr;
   S8              *endPtr;
   BASE_ADDRESS    baseAddr;
   BOOLEAN         noSymbolsLoaded;
   
   if (argc != 3)
      return(ER_CLI_SYNTAX);
   if (GOOD != (err = SymCheckForNoSymbols(&noSymbolsLoaded)))
      return err;
   if (noSymbolsLoaded) {
      return ER_SYMBOL_NOT_A_MODULE;  // reports "no symbols are loaded"
   }

   if ((err = GetBaseOffsetByName((LPSTR) &cmdString[(U16)argv[1]],
      baseOffset)) != GOOD)
      return err;
   // last param (0) means input determines radix; i.e. 0x = hex, 1-9 = dec
   baseAddr = strtoul((S8 *)&cmdString[(U16)argv[2]], &endPtr, 0);

   // endPtr should be pointing to last char of string; i.e. null, if string
   // conversion worked correctly
   if (*endPtr != 0) return ER_CLI_SYNTAX;
   basePtr = (BaseSymbol HUGE *)st.GetHugeDataPtr(baseOffset);
   basePtr->SetBaseAddr(baseAddr);
   
   // notify interested parties
   if((err = EnlEventNotify(EVENT_SYMBOL_BASE_CHANGED)) != GOOD) return err;

   return GOOD;
}


//--------------------------------------------------------------------------
// Count
//
// Displays the number of symbols in the various tables
//--------------------------------------------------------------------------
#pragma argsused
RETCODE EXPORT
Count(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE    err, firstErr;
   S8         str[80];
   U32        count, memSize, tableSize;
   TYPE_INDEX typeIndexMax;
   BOOLEAN    noSymbolsLoaded;

   if (argc != 1)
      return ER_CLI_SYNTAX;
   if (GOOD != (err = SymCheckForNoSymbols(&noSymbolsLoaded)))
      return err;
   if (noSymbolsLoaded) {
      return ER_SYMBOL_NOT_A_MODULE;  // reports "no symbols are loaded"
   }

   // ORDINAL
   count = ot.GetOrdinalCount();
   memSize = ot.GetAvailMemOffset();
   tableSize = ot.GetTableSize();
   sprintf((LPSTR)str, "Ordinal count = %ld  (%ld) [%ld]",
                        count, memSize, tableSize);
   firstErr = SendMessageToCli((LPU8)str);


   // HASH TABLE
   count = ht.GetEntryCount();
   memSize = ht.GetHashTableSize();
   tableSize = ht.GetTableSize();
   sprintf((LPSTR)str, "Global symbol count = %ld (%ld) [%ld]",
                        count, memSize, tableSize);
   err = SendMessageToCli((LPU8)str);
   if (firstErr == GOOD)
      firstErr = err;

   // LINENUMS
   count = lt.GetLinenumCount();
   memSize = lt.GetAvailMemOffset();
   tableSize = lt.GetTableSize();
   sprintf((LPSTR)str, "Line number count = %ld (%ld) [%ld]",
                        count, memSize, tableSize);
   err = SendMessageToCli((LPU8)str);
   if (firstErr == GOOD)
      firstErr = err;

   // TYPES
   err = tt.GetMaxTypeIndex(typeIndexMax);
   memSize = tt.GetAvailMemOffset();
   tableSize = tt.GetTableSize();
   if (firstErr == GOOD)
      firstErr = err;
   sprintf((LPSTR)str, "Type count = %ld (%ld) [%ld]",
                        typeIndexMax, memSize, tableSize);
   err = SendMessageToCli((LPU8)str);
   if (firstErr == GOOD)
      firstErr = err;

   // SYMBOL TABLE SIZE
   memSize = st.GetAvailMemOffset();
   tableSize = st.GetTableSize();
   sprintf((LPSTR)str, "Symbol table size = (%ld) [%ld]",
                        memSize, tableSize);
   firstErr = SendMessageToCli((LPU8)str);


   // BASE TABLE COUNT
   count = bit.BaseGetBaseCount();
   memSize = bit.GetTableSize();
   sprintf((LPSTR)str, "Base count = %ld (%ld)", count, memSize);
   err = SendMessageToCli((LPU8)str);
   if (firstErr == GOOD)
      firstErr = err;

   // SYMBOLS STATISTICS OF EACH SYM_TYPE_TYPE
   U8 symType, nameIndex;
   for (symType = 0; symType < MAX_SYM_TYPE; symType++) {
       nameIndex = symTypeMap[symType];
       if (nameIndex < MAX_SYM_TYPE) {
          sprintf((LPSTR)str, "%12s = %6ld symbols",
                  (LPSTR) symTypeName[nameIndex],
                  symbolStat[symType]);
          err = SendMessageToCli((LPU8)str);
          if (firstErr == GOOD) firstErr = err;
       }
   }            

   // BY-ADDRESS TABLE WALK
   {
   TABLE_OFFSET loopPtr;  // pass by reference
   S8           baseName[100];
   U32          entryCount;  // pass by reference
   BOOLEAN      startLoop = TRUE;
   RETCODE      localErr;
      while (TRUE) {
         localErr = ByAddressTableGetNext(startLoop, loopPtr,
                                          (LPSTR)baseName,
                                          entryCount,  // by reference
                                          tableSize);  // by reference
         if (localErr != GOOD)
            break;
         if (0 == strlen(baseName))
            strcat(baseName, "NoNameBase");
         memSize = entryCount * (sizeof(TABLE_OFFSET));
         sprintf((LPSTR)str, "  %s contains %ld symbols (%ld) [%ld]",
                  baseName, entryCount, memSize, tableSize);
         err = SendMessageToCli((LPU8)str);
         if (GOOD == firstErr)
            firstErr = err;
         startLoop = FALSE;
      }
   }
   return firstErr;
}  // end of Count


//--------------------------------------------------------------------------
// NameOf
//
// Converts an address into a symbol and displays it
//    Address can be numeric or "pc" which returns present pc value
//
// Design:
//    convert address text into an address descriptor
//    set up descriptor with symbolic mode on
//    call AdrConvAddressToText
//    output text to CLI
//--------------------------------------------------------------------------
#pragma argsused
RETCODE EXPORT
NameOf(LPSTR cmdString, U32 argc, U32 argv[])  {

   RETCODE    err, firstErr;
   DESCRIPTOR desc;
   LPSTR      lpParam;
   S8         str[ADDR_BUFF_SZ_SYMBOLIC];
   BOOLEAN    noSymbolsLoaded;

   if (argc != 2)
      return ER_CLI_SYNTAX;
   if (GOOD != (err = SymCheckForNoSymbols(&noSymbolsLoaded)))
      return err;
   if (noSymbolsLoaded) {
      return ER_SYMBOL_NOT_A_MODULE;  // reports "no symbols are loaded"
   }

   lpParam = &(cmdString[(U16)argv[1]]);
   while (isspace((S16)*lpParam)) {  /* skip over spaces; requires int */
      lpParam++;
   }
   if (isdigit((S16)*lpParam))  {
      // convert hex number to binary address
      if (GOOD != (err = AdrCreateAddress(&desc)))
         return err;
      if (GOOD != (err = AdrConvTextToAddress(desc, lpParam)))
         goto CLEANUP;
   } else if (stricmp(lpParam, (LPSTR)"pc")==0) {  // check for "pc" entered
      if (GOOD != (err = CpuGetPC(&desc)))
         return err;
   } else {
      return ER_CLI_SYNTAX;
   }

   if (GOOD != (err = AdrSetAddrSymbolUsage(desc, TRUE)))
      goto CLEANUP;
   if (GOOD != (err = AdrConvAddressToTextWithParams(desc, TRUE, FALSE, str)))
      goto CLEANUP;
   err = SendMessageToCli((LPU8)str);

CLEANUP:
   firstErr = err;
   err = AdrDestroyAddress(desc);
   if (GOOD == firstErr)
      firstErr = err;
   return firstErr;
} // end of NameOf


//--------------------------------------------------------------------------
// AddressOf
//
// Converts a symbol path and name into an address; uses #mod#func#var
//   naming convention
//--------------------------------------------------------------------------
RETCODE EXPORT
AddressOf(LPSTR cmdString, U32 argc, U32 argv[])  {

   RETCODE           err, nextErr;
   DESCRIPTOR        addrDesc = 0L;
   SYM_DESCRIPTOR    symDesc = 0L;
   LINENUM_TYPE      actualLinenum;
   COLUMN_RANGE_TYPE actualColumnRange;
   LPSTR             lpParam;
   S8                str[SYMCLIPR_ADDR_TEXT_WIDTH];
   S8                str2[SYMCLIPR_ADDR_TEXT_WIDTH];
   U32               offset;
   BOOLEAN           noSymbolsLoaded;

   if (argc != 2)
      return ER_CLI_SYNTAX;
   if (GOOD != (err = SymCheckForNoSymbols(&noSymbolsLoaded)))
      return err;
   if (noSymbolsLoaded) {
      return ER_SYMBOL_NOT_A_MODULE;  // reports "no symbols are loaded"
   }

   lpParam = &(cmdString[(U16)argv[1]]);
   while (isspace((S16)*lpParam)) {  /* skip over spaces; requires int */
      lpParam++;
   }
   err = SymGetAddrFromName(lpParam,
                            SYM_UNKNOWN_ADDR,
                            &symDesc,
                            &addrDesc,
                            &actualLinenum,
                            &actualColumnRange);
   /* NOTES: 09/22/93 - Nghia
   ** Filter out ER_NO_LINENUMS_ADDED to support ASM module which does not
   ** have any line number infomation, but still has a valid address range
   */
   if ((err != GOOD) && (err != ER_NO_LINENUMS_ADDED) &&
       (err != ER_ADR_END_ADDR_TOO_SMALL))
      return err;    // addrDesc not alloc'ed when error occurs

   if (actualLinenum == 0) { // indicates not a linenum lookup
      err = SymConvSymAddrToText(symDesc, str);
      // NOTES: 09/21/93 - Nghia
      // Only destroy addrDesc if it's valid
      if (addrDesc)
         goto CLEANUP1;
      // addrDesc is already destroyed, so do not destroying it
      goto CLEANUP2; 
   } else {
      if (GOOD != (err = AdrConvAddressToTextWithParams(addrDesc,
                                                        TRUE,
                                                        FALSE,
                                                        (LPSTR)str)))
         goto CLEANUP1;
      if (GOOD != (err = AdrGetEndAddrOffset(addrDesc, &offset)))
         goto CLEANUP1;
      if (GOOD != (err = AdrSetAddrOffset(addrDesc, offset)))
         goto CLEANUP1;
      if (GOOD != (err = AdrConvAddressToTextWithParams(addrDesc,
                                                        TRUE,
                                                        FALSE,
                                                        (LPSTR)str2)))
         goto CLEANUP1;
      strcat(str, "-");  // insert the "to" character
      strcat(str, str2);
 
   }
CLEANUP1:
   nextErr = AdrDestroyAddress(addrDesc);
   err = (err != GOOD) ? err : nextErr;
   
CLEANUP2:  
   nextErr = SendMessageToCli((LPU8)str);
   return((err != GOOD) ? err : nextErr);

} // end of AddressOf

//--------------------------------------------------------------------------
// DisplayTypes
//
// Displays all the types loaded.
//--------------------------------------------------------------------------
#pragma argsused
RETCODE EXPORT DisplayTypes(LPSTR cmdString, U32 argc, U32 argv[]) {
   S8                     str[80];
   S8                     typeName[MAX_SYMNAME_LENGTH];
   S8                     command[(MAX_SYMNAME_LENGTH/4)];
   U32                    memSize, tableSize;
   TYPE_INDEX             typeIndexMax, typeIndex;
   TYPE_INDEX             startIndex, endIndex;  
   BOOLEAN                noSymbolsLoaded, abortFromEsc;
   RETCODE                err = GOOD;
   TYPE_HEADER_TYPE2 HUGE *typeHdr;
   TYPE_HEADER_TYPE       typeHeader;
   TABLE_OFFSET           offset;
   
   if ((argc > 2) || (argc < 1))
      return ER_CLI_SYNTAX;
   if (GOOD != (err = SymCheckForNoSymbols(&noSymbolsLoaded)))
      return err;
   if (noSymbolsLoaded) {
      return ER_SYMBOL_NOT_A_MODULE;  // reports "no symbols are loaded"
   }
   // clears keyboard of any residual escape key hit
   if ((err = TskCheckAbort(&abortFromEsc)) != GOOD)
      return err;

   // TYPES TABLE INFORMATION
   err       = tt.GetMaxTypeIndex(typeIndexMax);
   memSize   = tt.GetAvailMemOffset();
   tableSize = tt.GetTableSize();
   // Set default endIndex = maxTypeIndex
   startIndex = 0L;
   endIndex = typeIndexMax;
   
   if (argc == 2) {
      strcpy(command, &cmdString[(U16)argv[1]]);
      if (!strcmpi(command, "built-in")) {
         endIndex = 255L;  // Actual last built-in type is 57
      } else if (!strcmpi(command, "user")) {
         startIndex = 256L;
      } else
         return ER_CLI_SYNTAX;
   }
      
   sprintf((LPSTR)str,
           "Max Type Index = %ld, MemSize = (%ld) TableSize = [%ld]",
           typeIndexMax, memSize, tableSize);
   SendMessageToCli((LPU8)str);
   typeHeader.typeName = (LPSTR) typeName;
   
   // Loop over all type and print the type out
   for (typeIndex = startIndex; typeIndex <= endIndex; typeIndex++) {
      // Use SymGetTypeHeader() to resolve any indirect reference type
      if ((err = SymGetTypeHeader(typeIndex, &typeHeader)) == GOOD) {
         sprintf((LPSTR)str, "-- TYPE INDEX: %ld", typeIndex);
         SendMessageToCli((LPU8)str);
         // Get more information about the type
         if ((err = typit.GetOffset(typeIndex, offset)) != GOOD)
            return(err);
         typeHdr = (TYPE_HEADER_TYPE2 HUGE *)tt.GetHugeDataPtr(offset);
         
         switch (typeHeader.typeChoice) {
            case SIMPLE_TYPE_CLASS:
               sprintf((LPSTR)str,
                       "Name = %s, Type Value = %#04X, SizeInMAUs = %ld",
                       (LPSTR)typeHeader.typeName,
                       (U8)typeHeader.t.simpleType,
                       (U32)typeHeader.sizeInMAUs);
               SendMessageToCli((LPU8)str);
               sprintf((LPSTR)str,
                       "Member = %d, Record size = %ld bytes",
                       (U16)typeHdr->memberCount,
                       (U32)typeHdr->recordSize);
               SendMessageToCli((LPU8)str);
               break;
               
            case COMPLEX_TYPE_CLASS:
               sprintf((LPSTR)str,
                       "Name = %s, Type Value = %#04X, SizeInMAUs = %ld",
                       (LPSTR)typeHeader.typeName,
                       (COMPLEX_TYPE)typeHeader.t.complexType,
                       (U32)typeHeader.sizeInMAUs);
               SendMessageToCli((LPU8)str);
               sprintf((LPSTR)str,
                       "Member = %d, Record size = %ld bytes",
                       (U16)typeHdr->memberCount,
                       (U32)typeHdr->recordSize);
               SendMessageToCli((LPU8)str);
               // Get information about the complex type
               switch(typeHeader.t.complexType) {
                  case TY_UNKNOWN :       // = 0x21  !
                     sprintf((LPSTR) str, "-- TY_UNKNOWN");
                     break;
                  case TY_ENUM_PASCAL :   // = 0x45  E
                     sprintf((LPSTR) str, "-- TY_ENUM_PASCAL");
                     break;
                  case TY_GENERAL :       // = 0x47  G
                     sprintf((LPSTR) str, "-- TY_GENERAL");
                     break;
                  case TY_ENUM_C:         // = 0x4E,  // N
                     sprintf((LPSTR) str, "-- TY_ENUM_C");
                     break;
                  case TY_SMALL_PTR :     // = 0x4F,  // O
                  {
                     TYPE_INDEX baseTypeIndex;
                     if (SymGetTypePointerTypeIndex(typeIndex, &baseTypeIndex)
                         == GOOD) {
                        sprintf((LPSTR)str,"BASE TYPE INDEX = %ld",
                                (TYPE_INDEX)baseTypeIndex);
                        SendMessageToCli((LPU8)str);
                     }
                     sprintf((LPSTR) str, "-- TY_SMALL_PTR");
                     break;
                  }

                  case TY_LARGE_PTR :     // = 0x50,  // P
                  {
                     TYPE_INDEX baseTypeIndex;
                     if (SymGetTypePointerTypeIndex(typeIndex, &baseTypeIndex)
                         == GOOD) {
                        sprintf((LPSTR)str,"BASE TYPE INDEX = %ld",
                                (TYPE_INDEX)baseTypeIndex);
                        SendMessageToCli((LPU8)str);
                     }
                     sprintf((LPSTR) str, "-- TY_LARGE_PTR");
                     break;
                  }

                  case TY_RANGE :         // = 0x52,  // R
                     sprintf((LPSTR) str, "-- TY_RANGE");
                     break;
                  case TY_STRUCT :        // = 0x53,  // S
                     sprintf((LPSTR) str, "-- TY_STRUCT");
                     break;
                  case TY_TYPE :          // = 0x54,  // T
                  {
                     TYPE_INDEX baseTypeIndex;
                     if (SymGetTypeTypeIndex(typeIndex, &baseTypeIndex)
                         == GOOD) {
                        sprintf((LPSTR)str,"BASE TYPE INDEX = %ld",
                                (TYPE_INDEX)baseTypeIndex);
                        SendMessageToCli((LPU8)str);
                     }
                     sprintf((LPSTR) str, "-- TY_TYPE");
                     break;
                  }
                  case TY_UNION :         // = 0x55,  // U
                     sprintf((LPSTR) str, "-- TY_UNION");
                     break;
                  case TY_VOID :          // = 0x56,  // V
                     sprintf((LPSTR) str, "-- TY_VOID");
                     break;
                  case TY_FUNC_NODEP :    // = 0x58,  // X
                     sprintf((LPSTR) str, "-- TY_FUNC_NODEP");
                     break;
                  case TY_C_ARRAY :       // = 0x5A,  // Z
                  {
                     TYPE_Z_STRUCT arrStruct;
                     if (SymGetTypeCArray(typeIndex, &arrStruct) == GOOD) {
                        if (arrStruct.highBound < 0)
                           sprintf((LPSTR)str,
                                   "external");
                        else
                           sprintf((LPSTR)str,
                                   "HighBound = %ld",
                                   (S32)arrStruct.highBound);
                        SendMessageToCli((LPU8)str);
                        sprintf((LPSTR)str,"BASE TYPE INDEX = %ld",
                                (TYPE_INDEX)arrStruct.typeIndex);
                        SendMessageToCli((LPU8)str);
                     }
                     sprintf((LPSTR) str, "-- TY_C_ARRAY");
                     break;
                  }
                  
                  case TY_BITFIELD :      // = 0x67,  // g
                  {
                     TYPE_BITFIELD_STRUCT bfStruct;
                     if (SymGetTypeBitfield(typeIndex, &bfStruct) == GOOD) {
                        sprintf((LPSTR) str,
                          "bitfieldSigned = %d, sizeInBits = %d",
                          (U8)bfStruct.bitfieldSigned,
                          (U16)bfStruct.size);
                        SendMessageToCli((LPU8)str);
                        sprintf((LPSTR)str,"BASE TYPE INDEX = %ld",
                                (TYPE_INDEX)bfStruct.baseTypeIndex);
                        SendMessageToCli((LPU8)str);
                     }
                     sprintf((LPSTR) str, "-- TY_BITFIELD");
                     break;
                  }
                  case TY_FUNC_DEP :      // = 0x78,  // x   
                     sprintf((LPSTR) str, "-- TY_FUNC_DEP");
                     break;
                  default :
                     sprintf((LPSTR)str, "Not supported type");
                     break;
               }
               SendMessageToCli((LPU8)str);  
               break;

            default :
               sprintf((LPSTR)str, "Unknown");
               SendMessageToCli((LPU8)str);
               
         } // end of switch
      }
      // escape when key pressed
      if ((TskCheckAbort(&abortFromEsc) != GOOD) || abortFromEsc) 
         return ER_ABORT_FROM_ESC;
   }
   return(err);
}
/******************************** E O F ***********************************/
