/****************************************************************************
**
**  Name:  varservr.c
**
**  Description:
**     External interface to the variable server.                  
**
**  $Log:   S:/tbird/mt2_186/varsrv/varservr.c_v  $
** 
**    Rev 1.2   04 Dec 1997 18:02:30   hera
** 
**    Rev 1.1   04 Dec 1997 16:19:24   hera
** For Test Report ID.88
** 
**    Rev 1.0   16 Dec 1996 14:48:02   Judy
** Initial revision.
** 
**    Rev 1.76   14 Jun 1995 11:52:42   marilyn
** Added call to ProcDefaultStackSize to init routine.
** 
**    Rev 1.75   07 Jun 1995 11:13:50   marilyn
** Fixed ppr 10472, inconsistent bitfield display.
** 
**    Rev 1.74   06 Jun 1995 10:31:26   nghia
** Fixed PPR10419. - Skip processing event when variable database is empty.
** 
**    Rev 1.73   18 May 1995 14:55:48   marilyn
** Fixed GPF when inspecting certain composite vars.
** 
**    Rev 1.72   16 May 1995 16:46:30   nghia
** Fixed PPR 10354 - inspect TY_VOID variable get error.
** Added TY_VOID to VarServer routines.
** 
**    Rev 1.71   01 May 1995 17:23:24   marilyn
** Fixed ppr 10214, 10215.  Limiting active variables to 100.
** 
**    Rev 1.70   28 Mar 1995 11:45:04   marilyn
** Updated IsPointer interface, modified EvaluateComponentVar to set
** dereferenced pointer var names to *X, fixes ppr 10103.
** 
**    Rev 1.69   24 Mar 1995 15:48:30   marilyn
** Removed module information requirement on GLOBAL vars in VarIsActive.
** 
**    Rev 1.68   10 Mar 1995 16:57:02   marilyn
** Fixed memory leak in IsActive routine.
** 
**    Rev 1.67   01 Feb 1995 14:27:56   marilyn
** Support new field invalidAddress for ppr 9961.
** 
**    Rev 1.66   24 Jan 1995 16:51:40   marilyn
** Added support for auto calculation of bitfield offsets for X86 bitfield
** support since the info is not right from the load file.  Also supports
** auto calculation via the ini file for HC16 Hicross compiled loadfiles.
** 
**    Rev 1.65   24 Oct 1994 11:47:18   marilyn
** fixed ppr 9964, some child vars not updating.
** 
**    Rev 1.64   17 Oct 1994 11:55:36   marilyn
** fixed ppr 9888, GPF for composite variables.
** 
**    Rev 1.63   12 Oct 1994 15:19:22   marilyn
** Fixed ppr 9626.
** 
**    Rev 1.62   08 Sep 1994 16:43:12   marilyn
** Fixed numerous problems with display, editing and dereferencing of pointers.
** Added support for some strange typedef combinations.  Special support for
** typedefs.
** 
**    Rev 1.61   24 Aug 1994 09:59:00   marilyn
** Updated for X86 support.
** 
**    Rev 1.60   20 Jun 1994 16:52:34   nghia
** Merged 1.59.1.0 to trunk.
** 
**    Rev 1.59.1.0   31 May 1994 17:30:18   nghia
** Added Pointer_type information to call to GetPtrDereference().
** 
**    Rev 1.59   08 Apr 1994 11:59:16   nghia
** Fixed bug for PV 2.2
** - Used AdrDuplicateAddress() instead of the AdrCreateAddress() to preserve
** the address information of context in comparison.
** 
**    Rev 1.58   03 Mar 1994 11:53:12   john
** Added CPU32+ handling
** 
**    Rev 1.57   28 Feb 1994 17:03:32   marilyn
** Moved maxOutput routines to the address server. Updated interfaces.
** 
**    Rev 1.56   01 Nov 1993 10:03:42   nghia
** Fixed PPR 8962 - Variable server handles SMALL/LARGE pointer types.
** Added call to retrieve MaxOutputAddress.
** 
**    Rev 1.55   06 Aug 1993 17:00:24   mindy
** Added changes for Cosmic
** 
**    Rev 1.54   04 Aug 1993 11:24:32   marilyn
** Fixed ppr 8691 where variable scope was screwed up for variables on the stack
** shown by the variable presenter.  Variable scope now reflects stack variables
** at their last known frame.  
** 
**    Rev 1.53   28 Jul 1993 14:23:46   marilyn
** Added initialization of name strings with memset to several routines
** to avoid any messy string problems.
** 
**    Rev 1.52   30 Jun 1993 11:30:30   mindy
** Removed the adjust offset routine - with the new information stored in
** in the stack structure no adjustment is needed. Other misc. changes.
** Also need to update the lifetime state for register'd variables for each
** current scope.
** 
**    Rev 1.51   19 May 1993 09:58:40   marilyn
** Added some clarifying comments to VarIsActive after discussion with Mindy.
** 
**    Rev 1.50   12 May 1993 16:36:52   ron
** variable window enhancments: pass position of edit text
** 
**    Rev 1.49   29 Mar 1993 11:41:08   nghia
** Fixed bug: Process typdef for array element.
** Added check for TY_TYPE to force the computation of var's line numbers to
** check for user-defined type and process the next element in the chain.
** 
**    Rev 1.48   08 Mar 1993 15:41:34   courtney
** Added helpful comments.
** 
**    Rev 1.47   12 Jan 1993 17:03:48   marilyn
** With the last changes to VarIsActive I had ruled out global vars
** that could be dereferenced.  The newest change checks the symbolic
** info as well to verify global scope.
** 
**    Rev 1.46   12 Jan 1993 10:26:20   marilyn
** Dereferenced ptrs are not considered active unless within the current
** context.  Changes to VarIsActive and UpdateVarTable.
** 
**    Rev 1.45   11 Jan 1993 15:58:12   marilyn
** commented out calls to checkError routine.
** 
**    Rev 1.44   04 Dec 1992 14:20:18   marilyn
** Fixed problem with expanding elements of arrays of enums. And editting
** enums in arrays.
** 
**    Rev 1.43   02 Dec 1992 13:44:42   marilyn
** Updated error code handling.
** 
**    Rev 1.42   11 Nov 1992 11:59:20   marilyn
** Changed LineForVar and BuffersForVar to U32 to support very large
** array indices.
** 
**    Rev 1.41   06 Nov 1992 16:16:52   marilyn
** Final beta bugs
** 
**    Rev 1.40   04 Nov 1992 17:38:46   marilyn
** Fixed pprs 7170,7183,7184,7198,7204,7305,7344,7345,7378,7435.
** 
**    Rev 1.39   27 Oct 1992 10:12:04   marilyn
** If a variable is deleted and then the presenter trys to delete it again
** the server just returns good, indicating that it is deleted.
** 
**    Rev 1.38   24 Oct 1992 10:42:48   marilyn
** Fixed beta bugs.
** 
**    Rev 1.37   22 Oct 1992 21:26:46   marilyn
** Fixed naming problem of dereferenced pts.
** 
**    Rev 1.36   21 Oct 1992 16:13:48   marilyn
** Fixed bug in dereferencing pointer addresses correctly.  Also some
** other bugs for beta.
** 
**    Rev 1.35   15 Oct 1992 17:51:34   marilyn
** Fixed several bugs for beta.
** 
**    Rev 1.34   09 Oct 1992 12:20:40   marilyn
** Removed define for debugging memory leaks.
** 
**    Rev 1.33   10 Sep 1992 15:27:00   marilyn
** Bug fixes in EvaluateComponentVar
**
**    Rev 1.31   03 Sep 1992 13:55:10   marilyn
** Fixed storage leak bugs.
** 
**    Rev 1.30   27 Aug 1992 10:37:00   marilyn
** Fixed bug in WalkVariableType which was causing UAE for pointer vars.
** 
**    Rev 1.29   04 Aug 1992 16:50:36   marilyn
** Removed childVarId field from variable table entries.
** 
**    Rev 1.28   31 Jul 1992 16:47:52   marilyn
** Added display of array indices for component vars.
** 
**    Rev 1.27   28 May 1992 16:31:38   marilyn
** Added VSReadStackVar.
**
**    Rev 1.26   03 Jan 1980 16:28:44   marilyn
** Fixed bug in EvaluateComponent where the storage type was changed to
** static.  Now it is set to global.
** 
**    Rev 1.25   15 May 1992 15:07:30   marilyn
** Fixed bug in VarIsActive where global vars were not displayed if
** the PC didn't map to a symbol.  Also the code now considers the static
** var case as well.
**
**    Rev 1.24   14 May 1992 16:30:06   marilyn
** Updated to set variables active level based on new flag which can
** be set from the CLI with togglevar command.
** 
**    Rev 1.23   13 May 1992 11:17:54   marilyn
** Added indexing for arrays.  Selectable array indices. 
**
**    Rev 1.22   08 May 1992 16:42:22   marilyn
** Added new interfaces, VSGetBufferSize and VSGetLinesForVar.
** 
**    Rev 1.21   06 May 1992 11:11:06   marilyn
** Added additional support for walking bitfield types.
** 
**    Rev 1.20   04 May 1992 17:08:50   marilyn
** Added additional support for bitfield types.
** 
**    Rev 1.19   29 Apr 1992 15:26:24   marilyn
** fixes bugs for displaying arrays of function ptrs
** 
**    Rev 1.18   28 Apr 1992 10:03:50   marilyn
** Fixed bug where anonymousClientInSession should have been !anonymousClient...
**
**    Rev 1.17   27 Apr 1992 15:46:32   marilyn
** Var server now registers on mem change event.
** 
**    Rev 1.16   24 Apr 1992 16:52:02   marilyn
** Fixed bugs related to typedef variables.
** 
**    Rev 1.15   21 Apr 1992 15:06:48   marilyn
** Fixed bug in VSEndSession which attempted to clear the table when
** it was empty.
** 
**    Rev 1.14   17 Apr 1992 16:12:40   marilyn
** Added call to StkCalcStkOffset.  Broke apart GetVarInfo into smaller
** pieces. Fixed bug in composite name format.
** 
**    Rev 1.13   15 Apr 1992 10:23:22   marilyn
** Fixed bug in calls to SymGetVar where the module and function descriptor
** parameters were flipped.  This caused the request for function names to
** fail.
** 
**    Rev 1.12   06 Apr 1992 11:14:26   marilyn
** Fixed bugs for looking at component type variables.
** 
**    Rev 1.11   03 Apr 1992 08:52:20   marilyn
** Fixed bugs dealing with multi-dimensional array types.
** 
**    Rev 1.10   30 Mar 1992 16:50:52   marilyn
** Modified UpdateVarTable.
** 
**    Rev 1.9   23 Mar 1992 08:58:32   marilyn
** Update interfaces to stack server, cpu server, and mem server.
** 
**    Rev 1.8   28 Feb 1992 10:56:30   marilyn
** Updated interfaces to memory and cpu servers.
** 
**    Rev 1.7   18 Feb 1992 09:57:00   marilyn
** Changed event define for the stack server event.
** 
**    Rev 1.6   04 Feb 1992 15:55:36   marilyn
** Fixed bug in GetNextAvailVarEntry.
**
**    Rev 1.5   27 Jan 1992 13:42:24   marilyn
** Fixed wrong number of parameters in call to EditValueField.
** 
**    Rev 1.4   23 Jan 1992 17:41:24   marilyn
** No change.
**
**    Rev 1.3   22 Jan 1992 17:33:06   marilyn
** Updates including CLI.
**
**    Rev 1.2   17 Jan 1992 15:54:20   marilyn
** Removed tester.h.  Using stubs.h instead.
** 
**    Rev 1.1   17 Jan 1992 08:03:22   marilyn
** integration version
**
**    Rev 1.0   16 Oct 1991 10:03:58   marilyn
** Initial revision.
**
**  $Header:   S:/tbird/mt2_186/varsrv/varservr.c_v   1.2   04 Dec 1997 18:02:30   hera  $
**
**  Copyright (C) 1991 Microtek International.  All rights reserved.
**
*****************************************************************************/

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


#ifndef _VARSERVR_
#include "varservr.h"
#endif

#ifndef _VARTABLE_
#include "vartable.h"
#endif

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

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

#ifndef _VARERRS_
#include "varerrs.h"
#endif

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

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

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

#ifndef _TEXTGEN_
#include "textgen.h"
#endif

#ifndef _VARUTIL_
#include "varutil.h"
#endif

#ifndef _TBIRDMEM_
#include "tbirdmem.h"
#endif

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

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

#ifndef _PROC_
#include "proc.h"
#endif

#ifndef _INIUTIL_
#include "iniutil.h"
#endif


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

#define DISPLAY_BUF_OVERHEAD (sizeof(VS_DISPLAY_BUFFER_TYPE))
#define NO_INDEX  0x7fff
#define INI_VAR_ENTRY "VariableInfo"
#define INI_VAR_AUTOCALC "AutoCalcBitfieldOffsets"


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

extern VS_STRING_TYPE charSetBuf;
extern HANDLE hLib;

                       
                        /****************************
                        *                          *
                        *     LOCAL STORAGE        *
                        *                          *
                        ****************************/


HWND hVarTable = NULL; /* variable table descriptor to array memory */
DESCRIPTOR descEvt;
BOOLEAN allVarActiveFlag = FALSE;
PROCESSOR_FAMILY procFamily;
ENDIAN_TYPE procEndian;
U32 procStackSize; 
U32 autoCalcBitOffset;  // read from ini file

/*
** #define DEBUG_MEM_LEAKS
*/
U32 memAlloced;
U32 memFreed;

PRIVATE U32  currVarTableIndex = 0;
U32  currVarTableSize = 0;
U32  varTableEntryCount = 0;

PRIVATE U32  clientEndIndex = 1;
PRIVATE BOOLEAN anonymousClientInSession = FALSE;

VS_VAR_TABLE_TYPE   varTable = NULL;
VS_CLIENT_TABLE_TYPE   clientTable = NULL;

PRIVATE BOOLEAN vsInitAlready = FALSE;
PRIVATE PROC_CPU cpu;

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

RETCODE PRIVATE AddTableEntry(U32 tableIndex,
                              DESCRIPTOR VSSessionId,
                              VS_VAR_SELECTION_TYPE *varInfo,
                              BOOLEAN interActive);

U32 PRIVATE ArrayLineLimit(TYPE_SUBHEADER_TYPE *typeInfo);

RETCODE PRIVATE AssignVarId(DESCRIPTOR VSSessionId,
                            DESCRIPTOR *varId);

U32 PRIVATE BuffersForVar(DESCRIPTOR varId);

RETCODE PRIVATE CleanTable(DESCRIPTOR VSSessionID);

BOOLEAN PRIVATE ClientRegistered(DESCRIPTOR clientHandle);

RETCODE PRIVATE DelTableEntry(DESCRIPTOR VSSessionId,
                              DESCRIPTOR VSVarId);

RETCODE PRIVATE DelVarTable(VOID);

RETCODE PRIVATE EvaluateComponentVar(VS_VAR_ENTRY_TYPE *rootVar,
                                     VS_COMPONENT_INFO_TYPE *compSym,
                                     TYPE_INDEX *typeIndex,
                                     LPU8 nameString,
                                     ARRAY_INDEX_NODE **arrayIndices,
                                     BOOLEAN *dereferencedPtr,
                                     BOOLEAN *typeDereferencedPtr,
                                     VAR_STORAGE_CLASS *storageClass,
                                     GET_VAR_ADDR_STRUCT *addr,
                                     U32 *byteOffset,                    
                                     U16 *bitOffset);

RETCODE PRIVATE FreeComponents(VS_COMPONENT_TYPE **compPtr);

RETCODE FreeVSBuf(VS_DISPLAY_PTR displayBufPtr);

RETCODE PRIVATE GetBaseTypeForArray(TYPE_SUBHEADER_TYPE *typeInfo);

RETCODE PRIVATE GetBaseTypeForBitfield(TYPE_SUBHEADER_TYPE *typeInfo);

RETCODE PRIVATE GetBaseTypeForTypedef(TYPE_SUBHEADER_TYPE *typeInfo);

RETCODE PRIVATE GetBitfield(TYPE_INDEX typeIndex,
                            U16 memberIndex,
                            VS_COMPONENT_TYPE *comp);

RETCODE PRIVATE GetBitfieldOffsetAdjustment(TYPE_SUBHEADER_TYPE *typeInfo,
                                            BOOLEAN sequentialBitfield,
                                            U16 *bitOffsetSumm,
                                            U16 *compOffset,
                                            U16 *compBitOffset);

RETCODE PRIVATE GetNextAvailClientEntry(U32 *availIndex);

RETCODE PRIVATE GetNextAvailVarEntry(U32 startIndex,
                                     U32 *availableIndex);

VOID PRIVATE GetNextToken(STRING *tokenPtr,
                          STRING token);

VOID PRIVATE GetSameToken(STRING tokenPtr,
                          STRING token);

RETCODE PRIVATE GetSorUComponents(VS_COMPONENT_TYPE **CompPtr,
                                    U16 memberCount,
                                    TYPE_INDEX typeIndex);

RETCODE PRIVATE GetTypeSignature(VS_VAR_ENTRY_TYPE *var);

RETCODE PRIVATE GetVSBuf(U32 size,
                         LPWORD *bufptr);

RETCODE GetVSdispBuf(DESCRIPTOR VSVarId,
                     DESCRIPTOR bufferNum,
                     U8 frameNum,
                     VS_DISPLAY_PTR  *dispPtr);

RETCODE PRIVATE GetVarSymbolInfo(U32 tableIndex,
                                 VS_VAR_SELECTION_TYPE *varInfo,
                                 BOOLEAN interActive);

U32 PRIVATE LinesForVar(DESCRIPTOR varId);

RETCODE PRIVATE OpenVariable(DESCRIPTOR VSSessionId,
                             VS_VAR_SELECTION_TYPE *varInfo,
                             BOOLEAN interActive,
                             DESCRIPTOR *VSVarId);

RETCODE PRIVATE PutVariableInTable(DESCRIPTOR VSSessionId,
                                   VS_VAR_SELECTION_TYPE *varInfo,
                                   BOOLEAN interActive,
                                   DESCRIPTOR *VSVarId);

VOID PRIVATE RecreateVarTable(VOID);

RETCODE PRIVATE StoreVarInfo(U32 tableIndex,
                          VS_VAR_SELECTION_TYPE *varInfo,
                          LPSTR varName,
                          TYPE_INDEX typeIndex,
                          BOOLEAN dereferencedPtr,
                          BOOLEAN typeDereferencedPtr,
                          VAR_STORAGE_CLASS storageClass,
                          VAR_REGISTER_CLASS registerClass,
                          BOOLEAN isConst,
                          SYM_DESCRIPTOR modDescriptor,
                          SYM_DESCRIPTOR funcDescriptor,
                          SYM_DESCRIPTOR parentDescriptor,
                          GET_VAR_ADDR_STRUCT *addr,
                          U32 byteOffset,
                          U16 bitOffset,
                          BOOLEAN interActive);

RETCODE PRIVATE UpdateVarTable(BOOLEAN *varTableChange);

BOOLEAN PRIVATE VarIsActive(U32 tableIndex,
                            CONTEXT_TYPE *varContext);

RETCODE PRIVATE  WalkVariableType(TYPE_INDEX *typeIndex,
                                  U8 frameType,
                                  GET_VAR_ADDR_STRUCT *addr,
                                  U16 *bitOffset,
                                  U32 *byteOffset,
                                  STRING *varNamePtr,
                                  VAR_STORAGE_CLASS *storageClass,
                                  VAR_REGISTER_CLASS *registerClass,
                                  BOOLEAN *dereferencedPtr);


                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/
/****************************************************************************
**
**   AddTableEntry :
**
**   Fill in the fields of the variable table entry.
**
*****************************************************************************/
RETCODE PRIVATE AddTableEntry(U32 tableIndex,
                              DESCRIPTOR VSSessionId,
                              VS_VAR_SELECTION_TYPE *varInfo,
                              BOOLEAN interActive)   {
   RETCODE retcode;

   retcode = GetVarSymbolInfo(tableIndex,varInfo,interActive);
   if ((retcode == GOOD) || ((retcode | E_SEVERITY_MASK) == E_WARNING)) {
      CheckWarning(retcode,&varTable[tableIndex].errorCode);
      varTable[tableIndex].used = TRUE;
      varTable[tableIndex].clientId = VSSessionId;
      varTableEntryCount++;
      clientTable[VSSessionId].varTableEntryCount++;
      return GOOD;
   }
   return retcode;
}

/************************************************************************
**
**   ArrayLineLimit :
**
************************************************************************/
U32 PRIVATE ArrayLineLimit(TYPE_SUBHEADER_TYPE *typeInfo) {

   TYPE_Z_STRUCT cArray;
   U16 memberCount;

   if (typeInfo->th.typeChoice == COMPLEX_TYPE_CLASS) {
      /* User defined type */
      if (typeInfo->th.t.complexType == TY_TYPE) {
         /*
         ** Process the next element in the chain and lets recheck
         ** for COMPLEX_TYPE_CLASS.
         */
         return(ArrayLineLimit(typeInfo->next));
      }
      /* Array type */
      if (typeInfo->th.t.complexType == TY_C_ARRAY) {
         SymGetTypeCArray(typeInfo->typeIndex,&cArray);
         return(HEADER_AND_FOOTER + ((cArray.highBound + 1)
               * ArrayLineLimit(typeInfo->next)));
      }
      else {
         SymGetTypeMemberCount(typeInfo->typeIndex,&memberCount);
         if (typeInfo->th.typeChoice == SIMPLE_TYPE_CLASS)
            return (memberCount);
         switch (typeInfo->th.t.complexType) {
            case TY_STRUCT :
            case TY_UNION :
               return (memberCount + HEADER_AND_FOOTER);
            case TY_SMALL_PTR :
            case TY_LARGE_PTR :
            case TY_16_16_PTR :
            case TY_16_32_PTR :
            case TY_ENUM_C :
               return (memberCount);
            default :
               return (memberCount);
         }
      }
   }
   SymGetTypeMemberCount(typeInfo->typeIndex,&memberCount);
   return (memberCount);
}

/***************************************************************************
**
**   AssignVarId :
**
**   This routine assigns an index to use for a variable id and encodes
**   the client's session id in the high byte.
**
***************************************************************************/
RETCODE PRIVATE AssignVarId(DESCRIPTOR VSSessionId,
                            DESCRIPTOR *varId)   {
   U32 availableIndex;
   RETCODE err;

   if ((err = GetNextAvailVarEntry(currVarTableIndex,&availableIndex)) != GOOD)
      return err;
   else
      *varId = (availableIndex | (VSSessionId << 24));
   return GOOD;
}

/****************************************************************************
**
**  BuffersForVar :
**
*******************************************************************************/
U32 PRIVATE BuffersForVar(DESCRIPTOR varId)  {
   U32 numlines;

   numlines = LinesForVar(varId);
   return((numlines/VS_MAX_BUFFER_LINES)+(((numlines%VS_MAX_BUFFER_LINES)>0)?1:0));
}

/**************************************************************************
**
**   CleanTable :
**
**   Remove any entries from the table corresponding to the client id that
**   are still in use.
**
****************************************************************************/
RETCODE PRIVATE CleanTable(DESCRIPTOR VSSessionId)  {
   LOOP_VAR i;
   RETCODE error;

   for (i = 0;i < currVarTableSize;i++) {
      if (varTable[i].used && (varTable[i].clientId == VSSessionId))  {
         if ((FreeComponents(&varTable[i].compListPtr)) != GOOD)
            varTable[i].compListPtr = NULL;  /* just keep going */
         if ((FreeSubTypes(&varTable[i].typeInfo)) != GOOD)
            varTable[i].typeInfo.next = NULL;
         if (varTable[i].address.getFixedAddrDesc != NULL)  {
            if ((error = AdrDestroyAddress(
                  varTable[i].address.getFixedAddrDesc)) != GOOD)
             return error;
         }
         varTable[i].used = FALSE;
         varTableEntryCount--;
         clientTable[VSSessionId].varTableEntryCount--;
      }
   }
   return GOOD;
}

/**************************************************************************
**
**   ClientRegistered :
**
**   If the input client is already registered in the client table then
**   return TRUE
**
**   Inputs:
**      clientHandle:  client's window id
**   Outputs:
**	none
****************************************************************************/
BOOLEAN PRIVATE ClientRegistered(DESCRIPTOR clientHandle)   {
   LOOP_VAR i=1;

   while ((clientHandle != 0) && (i <= clientEndIndex))  {
      if ((clientTable[i].used) &&
          (clientTable[i].winHandle == clientHandle))
         return TRUE;
      i++;
   }
   return FALSE;
}

/******************************************************************************
**
**   DelTableEntry :
**
**   Check for access rights and set the table entry to unused.
**
******************************************************************************/
RETCODE PRIVATE DelTableEntry(DESCRIPTOR VSSessionId,
                              DESCRIPTOR VSVarId)   {
   RETCODE error;

   if ((VSVarId >> 24) != VSSessionId)
      return ER_VS_ACCESS_DENIED;
   VSVarId &= VS_VARID_MASK;
   if (!varTable[VSVarId].used)
      return GOOD;            /* already been deleted */
   varTable[VSVarId].used = FALSE;
   varTableEntryCount--;
   currVarTableIndex = VSVarId;
   clientTable[VSSessionId].varTableEntryCount--;
   if ((error = FreeComponents(&varTable[VSVarId].compListPtr)) != GOOD)
      return error;
   if ((error = FreeSubTypes(&varTable[VSVarId].typeInfo)) != GOOD)
      return error;
   if (varTable[VSVarId].address.getFixedAddrDesc != NULL)  {
      if ((error = AdrDestroyAddress(
            varTable[VSVarId].address.getFixedAddrDesc)) != GOOD)
         return error;
      varTable[VSVarId].address.getFixedAddrDesc = 0;
   }

   return GOOD;
}

/**************************************************************************
**
**  DelVarTable
**
**  Remove all entries from variable table.  In effect is a close session
**  for session holders.
**
****************************************************************************/
RETCODE PRIVATE DelVarTable(VOID) {

   LOOP_VAR i;

   for (i = 1;i <= clientEndIndex;i++) {
      if ((clientTable[i].used) && (varTable != NULL))
         CleanTable(i);
   }
   return GOOD;
}

/***************************************************************************
**
**  EvaluateComponentVar
**
**  only structs, unions, pointers, enums and arrays of the
**  structs, unions, pointers and enums can have components
**  and don't forget typedefs of all of the above.
**  Example (struct.field or union.field or struct.*ptr.field)
**
**  Parameters:
**      rootVar (in) : root variable entry in var table
**      compSym (in) : presenter provided component type information like
**                     buffer number and line number
**      typeIndex (in/out) :  typeIndex, if typedefed the base typeIndex
**                            is returned
**      nameString (in) : the component part variable name
**      arrayIndices (out) : linked list returning any array indices, used 
**                           only for array types
**      dereferencedPtr (out) : the component is dereferenced     
**      typeDereferencedPtr (out) : resulting type information is always
**                                  dereferencing in nature
**      storageClass (out) : resulting storage class of the component
**      addr (out) : resulting base address of component (mem/stack/register)
**      byteOffset (out) : resulting byte offset from base address
**      bitOffset (out) : resulting bit offset from base address and 
**                        byte offset
**
****************************************************************************/
RETCODE PRIVATE EvaluateComponentVar(VS_VAR_ENTRY_TYPE *rootVar,
                                     VS_COMPONENT_INFO_TYPE *compSym,
                                     TYPE_INDEX *typeIndex,
                                     LPU8 nameString,
                                     ARRAY_INDEX_NODE **arrayIndices,
                                     BOOLEAN *dereferencedPtr,
                                     BOOLEAN *typeDereferencedPtr,
                                     VAR_STORAGE_CLASS *storageClass,
                                     GET_VAR_ADDR_STRUCT *addr,
                                     U32 *byteOffset,
                                     U16 *bitOffset) {

   VS_COMPONENT_TYPE compInfo; // contains symbolic TYPE_S_U_STRUCT  
   U16 memberIndex = NO_INDEX;
   BOOLEAN noMatch;
   ARRAY_LIST_NODE *arrayNode;
   TYPE_SUBHEADER_TYPE *baseTypeInfo; // contains symbolic TYPE_HEADER_TYPE 
   TYPE_SUBHEADER_TYPE typeInfo;      // contains symbolic TYPE_HEADER_TYPE 
   TYPE_SUBHEADER_TYPE *typeTypeInfo; // contains symbolic TYPE_HEADER_TYPE 
   RETCODE error;

   memset(&compInfo,'\0',sizeof(compInfo));
   compInfo.varName = (LPSTR)nameString;
   compInfo.typeInfo.th.typeName = (LPSTR)compInfo.typeInfo.typeNameStr;
   movmem(&rootVar->typeInfo,&typeInfo,sizeof(typeInfo));
   typeInfo.th.typeName = (LPSTR)typeInfo.typeNameStr;
   *byteOffset = 0L;
   *bitOffset = 0;
   *dereferencedPtr = FALSE;
   *typeDereferencedPtr = FALSE;

   /*
   ** start with the typeIndex of the root var
   */
   *typeIndex = typeInfo.typeIndex;

   while (typeInfo.th.t.complexType == TY_TYPE) {
      if ((error = SymGetTypeTypeIndex(*typeIndex,typeIndex)) != GOOD)
         return error;
      if ((error = SymGetTypeHeader(*typeIndex,
            (TYPE_HEADER_TYPE *)&typeInfo)) != GOOD)
         return error;
      typeInfo.typeIndex = *typeIndex;
      typeInfo.next = NULL;
   }
   switch (typeInfo.th.t.complexType)  {
      case TY_C_ARRAY:
         if ((error = MapArrayLine2Offset(&typeInfo,
               ((compSym->bufferNum * VS_MAX_BUFFER_LINES) +
               compSym->lineNum),0,byteOffset,&memberIndex,typeIndex,
               &arrayNode,arrayIndices)) != GOOD)
            return error;
         FindSubArrayType(&typeInfo,*arrayIndices,&baseTypeInfo);
         CheckForTypedef(baseTypeInfo,&typeTypeInfo,&baseTypeInfo);
         if ((baseTypeInfo->th.typeChoice == COMPLEX_TYPE_CLASS) &&
              ((baseTypeInfo->th.t.complexType == TY_STRUCT) ||
               (baseTypeInfo->th.t.complexType == TY_UNION))) {
            if (memberIndex != NO_INDEX) {
               if ((error = SymGetTypeStructUnionNth(baseTypeInfo->
                     typeIndex,memberIndex,
                     (TYPE_S_U_STRUCT *)&compInfo)) != GOOD)
                  return error;
               *typeIndex = compInfo.typeIndex;
            }
            else
               *typeIndex = baseTypeInfo->typeIndex;
            *storageClass = rootVar->storageClass;
            if ((error = GetComponentAddress(rootVar,typeIndex,*byteOffset,
                   addr,storageClass,dereferencedPtr)) != GOOD)
               return error;
         }
         if ((baseTypeInfo->th.typeChoice == COMPLEX_TYPE_CLASS) &&
              ((baseTypeInfo->th.t.complexType == TY_C_ARRAY) ||
               (baseTypeInfo->th.t.complexType == TY_ENUM_C)))  {
            *typeIndex = baseTypeInfo->typeIndex;
            *storageClass = rootVar->storageClass;
            if ((error = GetComponentAddress(rootVar,typeIndex,*byteOffset,
                   addr,storageClass,dereferencedPtr)) != GOOD)
               return error;
         }
         if ( IsPointer(&baseTypeInfo->th)) {
            /*
            ** must be a pointer to some type
            */
            *typeIndex = baseTypeInfo->typeIndex;
            if ((error = GetComponentAddress(rootVar,typeIndex,*byteOffset,
                   addr,storageClass,dereferencedPtr)) != GOOD)
               return error;
         }
         if (baseTypeInfo->th.typeChoice == SIMPLE_TYPE_CLASS) {
            *storageClass = rootVar->storageClass;
            *typeIndex = baseTypeInfo->typeIndex;
            if ((error = GetComponentAddress(rootVar,typeIndex,*byteOffset,
                   addr,storageClass,dereferencedPtr)) != GOOD)
               return error;
         }
         if (*dereferencedPtr)
            *typeDereferencedPtr = TRUE;

         break;

      case TY_STRUCT:
      case TY_UNION: {
         VS_STRING_TYPE typeName;
         VS_STRING_TYPE tempStr;

         typeName[0] = '\0';
         tempStr[0] = '\0';
         strcpy((LPSTR)nameString,(compSym->varString+1));
         nameString[(strlen((LPSTR)nameString)-1)] = '\0';
         if ((error = SymGetTypeStructUnion(*typeIndex, (LPSTR)nameString,
               (TYPE_S_U_STRUCT *)&compInfo,&memberIndex,&noMatch)) != GOOD)
            return error;
         if (noMatch)
            return ER_VS_VARNAME_NOT_FOUND;
         /*
         **  adjust the addr by compInfo.offset but
         **  first you need to determine if it is in bytes
         **  or bits.
         */
         if ((error = SymGetTypeHeader(compInfo.typeIndex,
               (TYPE_HEADER_TYPE *)&compInfo.typeInfo)) != GOOD)
            return error;
         compInfo.typeInfo.typeIndex = compInfo.typeIndex;
         if ((compInfo.typeInfo.th.typeChoice == COMPLEX_TYPE_CLASS) &&
               (compInfo.typeInfo.th.t.complexType == TY_BITFIELD)) {
            if ((error = GetBitfield(*typeIndex, 
                                     memberIndex,
                                     &compInfo)) != GOOD)
               return error;
            *bitOffset = compInfo.bitOffset;
         }
         *byteOffset = compInfo.offset;
         *typeIndex = compInfo.typeIndex;
         *storageClass = rootVar->storageClass;
         if ((error = GetComponentAddress(rootVar,typeIndex,*byteOffset,
               addr,storageClass,dereferencedPtr)) != GOOD)
            return error;
         if (*dereferencedPtr) {
            /* the component is dereferenced so show this by using
            ** the *VARNAME protocol.
            */
            strcpy((LPSTR)tempStr,(LPSTR)nameString);
            strcpy((LPSTR)nameString,"*");
            strcat((LPSTR)nameString,(LPSTR)tempStr);
         }
         if (rootVar->dereferencedPtr) {
            /*
            **  then the component of the root is also a dereferenced
            **  ptr regardless of its base type.  astruct->field
            */
            if (*dereferencedPtr)
               *typeDereferencedPtr = TRUE;
            *dereferencedPtr = TRUE;
         }
         break;
      }
      case TY_SMALL_PTR:
      case TY_LARGE_PTR:
      case TY_16_16_PTR:
      case TY_16_32_PTR:
         /*
         ** expandable pointers can be auto, static, or registers
         ** must calculate the address of where the pointer points to
         ** based on storage and register class.
         */
         if ((error = GetPtrDereference(rootVar->registerClass,
               rootVar->storageClass,
               &typeInfo, typeInfo.th.t.complexType, 
               0,rootVar->frameNum,
               rootVar->frameType,
               &rootVar->address,addr)) != GOOD)
            return error;
         if ((error = SymGetTypePointerTypeIndex(typeInfo.typeIndex,
               typeIndex)) != GOOD)
            return error;
         *dereferencedPtr = TRUE;
         *typeDereferencedPtr = TRUE;
         *storageClass = GLOBAL_VAR_CLASS;
         break;
   }

   return GOOD;
}
/*****************************************************************************
**
**  FreeComponents :
**
**  Deallocate any linked components for the variable table entry.
**
*******************************************************************************/
RETCODE PRIVATE FreeComponents(VS_COMPONENT_TYPE **compPtr)  {
   VS_COMPONENT_TYPE *component;
   RETCODE error;

   component = *compPtr;
   while (component != NULL)  {
      *compPtr = component->next;
      if ((error = FreeSubTypes(&component->typeInfo)) != GOOD)
         return error;
      if ((error = TFree((LPSTR)component)) != GOOD)
         return error;
      component = *compPtr;
   }
   return GOOD;
}


/*****************************************************************************
**
**  FreeSubTypes :
**
**  Deallocate any linked subtype information for the variable table entry.
**
*******************************************************************************/
RETCODE FreeSubTypes(TYPE_SUBHEADER_TYPE *typeInfo)  {
   TYPE_SUBHEADER_TYPE FAR *type;
   RETCODE error;

   type = typeInfo->next;
   while (type != NULL)  {
      type = type->next;
      if ((error = TFree((LPSTR)typeInfo->next)) != GOOD)
         return error;
      typeInfo->next = type;
   }
   return GOOD;
}

/***********************************************************************
**
**
**
*************************************************************************/
RETCODE FreeVSBuf(VS_DISPLAY_PTR displayBufPtr) {

   LPWORD bufptr;
   RETCODE error;

   bufptr = (LPWORD)displayBufPtr;
   if (*bufptr == VS_TMALLOC) {
      if ((error = TFree((LPSTR)displayBufPtr)) != GOOD)
         return error;
   }
   else
      GlobalFree(*bufptr);

   return GOOD;
}
/************************************************************************
**
**  GetBaseTypeForArray :
**
**  Recursive routine to get to the base type of an array.
**
**************************************************************************/
RETCODE PRIVATE GetBaseTypeForArray(TYPE_SUBHEADER_TYPE *typeInfo)  {

   TYPE_Z_STRUCT cArray;
   TYPE_SUBHEADER_TYPE  *subtype;
   RETCODE error;

   if ((subtype=(TYPE_SUBHEADER_TYPE *)TMalloc(sizeof(*subtype))) == NULL)
      return ER_OUT_OF_MEMORY;
   subtype->next = NULL;
   typeInfo->next = subtype;
   subtype->th.typeName = (LPSTR)subtype->typeNameStr;
   memset(subtype->th.typeName,'\0',sizeof(subtype->typeNameStr));
   if ((error = SymGetTypeCArray(typeInfo->typeIndex,&cArray)) != GOOD)
      return error;
   if ((error = SymGetTypeHeader(cArray.typeIndex,
         (TYPE_HEADER_TYPE *)subtype)) != GOOD)
      return error;
   subtype->typeIndex = cArray.typeIndex;
   if (subtype->th.typeChoice == COMPLEX_TYPE_CLASS) {
      switch (subtype->th.t.complexType)  {
         case TY_C_ARRAY :
            if ((error = GetBaseTypeForArray(subtype)) != GOOD)
               return error;
            break;
         case TY_SMALL_PTR :
         case TY_LARGE_PTR :
         case TY_16_16_PTR :
         case TY_16_32_PTR :
            if ((error = GetBaseTypeForPointer(subtype)) != GOOD)
               return error;
            break;
         case TY_TYPE:
            if ((error = GetBaseTypeForTypedef(subtype)) != GOOD)
               return error;
            break;
         default :
            break;
      }
   }
   return GOOD;
}

/************************************************************************
**
**  GetBaseTypeForBitfield :
**
**  Recursive routine to get to the base type of a bitfield.
**
**************************************************************************/
RETCODE PRIVATE GetBaseTypeForBitfield(TYPE_SUBHEADER_TYPE *typeInfo)  {

   TYPE_SUBHEADER_TYPE *subtype;
   RETCODE error;

   if ((subtype=(TYPE_SUBHEADER_TYPE FAR *)TMalloc(sizeof(*subtype))) == NULL)
      return ER_OUT_OF_MEMORY;
   subtype->next = NULL;
   typeInfo->next = subtype;
   subtype->th.typeName = (LPSTR)subtype->typeNameStr;
   memset(subtype->th.typeName,'\0',sizeof(subtype->typeNameStr));
   if ((error = SymGetTypeBitfield(typeInfo->typeIndex,
         (TYPE_BITFIELD_STRUCT *)&typeInfo->bitfieldInfo)) != GOOD)
      return error;
/*
   switch (typeInfo->bitfieldInfo.size) {
      case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
         typeInfo->size = 1;             // use the min # of bytes to //
         break;                          // encompass the # of bits   //
      case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16:
         typeInfo->size = 2;
         break;
      default:
         typeInfo->size = 4;
         break;
   }
*/
   subtype->typeIndex = typeInfo->bitfieldInfo.bField.baseTypeIndex;
   if ((error = SymGetTypeHeader(subtype->typeIndex,
         (TYPE_HEADER_TYPE *)subtype)) != GOOD)
      return error;
   if (subtype->th.typeChoice == COMPLEX_TYPE_CLASS) {
      switch (subtype->th.t.complexType)  {
         case TY_TYPE :
            if ((error = GetBaseTypeForTypedef(subtype)) != GOOD)
               return error;
            break;
          default :
            break;
      }
   }
   return GOOD;
}

/************************************************************************
**
**  GetBaseTypeForPointer :
**
**  Recursive routine to get to the base type of a pointer.
**
**************************************************************************/
RETCODE GetBaseTypeForPointer(TYPE_SUBHEADER_TYPE *typeInfo)  {

   TYPE_SUBHEADER_TYPE *subtype;
   RETCODE error;

   if ((subtype=(TYPE_SUBHEADER_TYPE FAR *)TMalloc(sizeof(*subtype))) == NULL)
      return ER_OUT_OF_MEMORY;
   subtype->next = NULL;
   typeInfo->next = subtype;
   subtype->th.typeName = (LPSTR)subtype->typeNameStr;
   memset(subtype->th.typeName,'\0',sizeof(subtype->typeNameStr));
   if ((error = SymGetTypePointerTypeIndex(typeInfo->typeIndex,
         &subtype->typeIndex)) != GOOD)
      return error;
   if ((error = SymGetTypeHeader(subtype->typeIndex,
         (TYPE_HEADER_TYPE *)subtype)) != GOOD)
      return error;
   if (subtype->th.typeChoice == COMPLEX_TYPE_CLASS) {
      switch (subtype->th.t.complexType)  {
         case TY_SMALL_PTR :
         case TY_LARGE_PTR :
         case TY_16_16_PTR :
         case TY_16_32_PTR :
            if ((error = GetBaseTypeForPointer(subtype)) != GOOD)
               return error;
            break;
         case TY_TYPE:
            if ((error = GetBaseTypeForTypedef(subtype)) != GOOD)
               return error;
            break;
         default :
            break;
      }
   }
   return GOOD;
}

/************************************************************************
**
**  GetBaseTypeForTypedef :
**
**  Recursive routine to get to the base type of a typedef.
**
**************************************************************************/
RETCODE PRIVATE GetBaseTypeForTypedef(TYPE_SUBHEADER_TYPE *typeInfo)  {

   TYPE_SUBHEADER_TYPE *subtype;
   RETCODE error;

   if ((subtype=(TYPE_SUBHEADER_TYPE FAR *)TMalloc(sizeof(*subtype))) == NULL)
      return ER_OUT_OF_MEMORY;
   subtype->next = NULL;
   typeInfo->next = subtype;
   subtype->th.typeName = (LPSTR)subtype->typeNameStr;
   memset(subtype->th.typeName,'\0',sizeof(subtype->typeNameStr));
   if ((error = SymGetTypeTypeIndex(typeInfo->typeIndex,
         &subtype->typeIndex)) != GOOD)
      return error;
   if ((error = SymGetTypeHeader(subtype->typeIndex,
         (TYPE_HEADER_TYPE *)subtype)) != GOOD)
      return error;
   if (subtype->th.typeChoice == COMPLEX_TYPE_CLASS) {
      switch (subtype->th.t.complexType)  {
         case TY_SMALL_PTR :
         case TY_LARGE_PTR :
         case TY_16_16_PTR :
         case TY_16_32_PTR :
            if ((error = GetBaseTypeForPointer(subtype)) != GOOD)
               return error;
            break;
         case TY_C_ARRAY :
            if ((error = GetBaseTypeForArray(subtype)) != GOOD)
               return error;
            break;
         case TY_TYPE :
            if ((error = GetBaseTypeForTypedef(subtype)) != GOOD)
               return error;
            break;
          default :
            break;
      }
   }
   return GOOD;
}

/***************************************************************************
**
** GetBitfield
**
** Bitfields occur in structs and union types.  X86 and some HC16
** compilers do not provide the correct bitoffset information, so we
** have to figure it out for ourselves.  Since bit offset information
** is dependent on the surrounding field information we have to
** process the entire structure's fields up to the determined
** member index.
**
** Parameters:
**      typeIndex (in) : typeIndex of root struct or union
**      memberIndex (in) : memberIndex of bitfield within root var
**      comp (in/out) : resulting component information of the bitfield
**
**  Algorithm:
**    1. separate bitfields that need address calculation from
**       those that don't by processor family and auto calc flag.
**    2. For bitfields requiring address calculation:
**          the provided component information does not contain 
**          addressing information, in order to calculate bitfield
**          addressing you start at the beginning field of the root
**          and accumulate the address information in bitOffset until
**          you find the matching memberIndex of the bitfield.
**   3. For bitfields not requiring address calculation because it
**      is already provided call to get the appropriate offset for
**      memory reads. 
**
**************************************************************************/
RETCODE PRIVATE GetBitfield(TYPE_INDEX typeIndex,            
                            U16 memberIndex,
                            VS_COMPONENT_TYPE *comp) {

   U16 bitOffset = 0;
   LOOP_VAR i;
   BOOLEAN prevCompBitfield = FALSE;
   RETCODE err = GOOD;

   if ((procFamily == FAMILY_X86) || autoCalcBitOffset) {
      // start with first field of root working up to memberIndex
      for (i=0;i <= memberIndex;i++) {
         // reuse provided component
         memset(comp,'\0', sizeof(*comp));
         comp->typeInfo.th.typeName = (LPSTR)comp->typeInfo.typeNameStr;
         comp->varName = (LPSTR)comp->varNameStr;
         // get field nth info and subsequent type info too
         if ((err = SymGetTypeStructUnionNth(typeIndex,i,
               (TYPE_S_U_STRUCT *)comp)) != GOOD)
            return err;
         if ((err = SymGetTypeHeader(comp->typeIndex,
               (TYPE_HEADER_TYPE *)&comp->typeInfo)) != GOOD)
            return err;
         comp->typeInfo.typeIndex = comp->typeIndex;
         if ((comp->typeInfo.th.typeChoice == COMPLEX_TYPE_CLASS)
               && (comp->typeInfo.th.t.complexType == TY_BITFIELD)) {
            // we found a field that is a bitfield, call to
            // adjust the bit offset into a memory readable
            // address (bytes and bits)
            if ((err = GetBitfieldOffsetAdjustment(&comp->typeInfo,
                  prevCompBitfield, &bitOffset,
                  &comp->offset,&comp->bitOffset)) != GOOD)
               return err;
            prevCompBitfield = TRUE;
         }
         else {
            prevCompBitfield = FALSE;
         }
      } // end for
      return err;
   }
   else
      return(GetBitfieldOffsetAdjustment(&comp->typeInfo,
                                  TRUE,&bitOffset,
                                  &comp->offset,
                                  &comp->bitOffset));
}

/**************************************************************************
**
** GetBitfieldOffsetAdjustment
**
**  This routine adjusts the bitfield offset from N bits into 
**  the appropriate memory readable address of 
**  bytes and bits depending on the base type size of the bitfield
**  processor type and autoCalc flag from ini file.
**
**  Parameters:
**      typeInfo (in) : bitfield type information
**      sequentialBitfield (in) : indicates accumulating total
**      bitOffsetSumm (in/out) : total bitfield summation
**      compOffset (in/out) : input bit offset/resulting byte offset
**      compBitOffset (out) : resulting bit offset
**      
****************************************************************************/
RETCODE PRIVATE GetBitfieldOffsetAdjustment(TYPE_SUBHEADER_TYPE *typeInfo,
                                            BOOLEAN sequentialBitfield,
                                            U16 *bitOffsetSumm,
                                            U16 *compOffset,
                                            U16 *compBitOffset) {

   U16 bitfieldOffset = 0;
   RETCODE err;

   if ((err = GetBaseTypeForBitfield(typeInfo)) != GOOD)
      return err;
   if ((procFamily == FAMILY_X86) || (autoCalcBitOffset)) {
      // We have to adjust the compOffset since it is
      // not calculated for X86 or Hiware(HC16).
      U32 baseTypeSize, bitfieldSize;
      if ((!sequentialBitfield) && (*compOffset != 0))
         // With non sequential bitfield you have to start with
         // last byte offset from compOffset and turn it back
         // into bits
         *bitOffsetSumm = *compOffset * BITS_PER_BYTE;
      // starting off point
      *compOffset = *bitOffsetSumm;
      baseTypeSize = typeInfo->th.sizeInMAUs * BITS_PER_BYTE;
      bitfieldSize = typeInfo->bitfieldInfo.bField.size;
      // in other words is there room for this bitfield in the
      // current base type size usually 2 or 4 bytes
      if (((*bitOffsetSumm % baseTypeSize) + bitfieldSize) > 
            baseTypeSize) {
         // no room for bitfield so pad out to baseType and
         // align bitfield to next baseTypeSize boundary
         U16 wastedBits;
         wastedBits = baseTypeSize - (*bitOffsetSumm % baseTypeSize);
         if (wastedBits < bitfieldSize) {
            // don't forget to include the pad fields in the total
            *bitOffsetSumm += wastedBits;
            *compOffset = *bitOffsetSumm;
         }
      }
      // accumulate this bitfield into the total 
      *bitOffsetSumm += bitfieldSize;
   }
   // now turn the total bits into bytes and bits for mem reading
   bitfieldOffset = *compOffset;
   typeInfo->bitfieldInfo.bitOffset = *compOffset;
          
   if (bitfieldOffset >= (typeInfo->th.sizeInMAUs * BITS_PER_BYTE)) {
      // this bitfield in subsequent baseTypeSize memory chunk
      U32 adjustBytes = 0;
      // turn into bytes and bits
      *compOffset = bitfieldOffset / BITS_PER_BYTE;
      *compBitOffset = bitfieldOffset % BITS_PER_BYTE;
      // check for basetype alignment
      adjustBytes = *compOffset % typeInfo->th.sizeInMAUs;
      // check for work alignment
      adjustBytes %= 2;   // word align
      if (adjustBytes) {
         // uneven base type access, make adjustments
         // ie. 3 bytes/3 bits ==> 2 bytes/11 bits
        *compOffset -= adjustBytes;
        *compBitOffset += (adjustBytes * BITS_PER_BYTE);
      }
   }
   else {
      // this bitfield in first baseTypeSize memory chunk 
      *compOffset = 0;
      *compBitOffset = bitfieldOffset;
   }
   return err;
}

/***************************************************************************
**
**   GetNextAvailClientEntry :
**
**   This routine searches the client table starting at 1 looking for an
**   unused entry.  The last actual entry index is saved so that the entire
**   table does not have to be searched every time.  The zero entry is
**   reserved for anonymous clients.
**
**   Inputs:
**      none
**   Outputs:
**      availIndex : the next available index location
****************************************************************************/
RETCODE PRIVATE GetNextAvailClientEntry(U32 *availIndex)   {
   LOOP_VAR i=1;

   while ((i < VS_MAX_CLIENTS) && (clientTable[i].used)) i++;
   if (i == VS_MAX_CLIENTS)
      return ER_VS_NO_SPACE_TABLE;
   *availIndex = i;
   if (*availIndex > clientEndIndex)
      clientEndIndex = *availIndex;
   return GOOD;
}

/*****************************************************************************
**
**   GetNextAvailVarEntry :
**
**   Gets the next available unused entry in the variable watch table. If
**   there is not enough room then the appropriate routines are called to
**   allocate a larger table.  The search strategy is to start somewhere
**   in the table and to search to the end, if still no entry then start
**   at the beginning and search til the starting point.  If there still
**   isn't any space then make the table bigger and if you can't do that
**   then you're SOL.
**
**   Inputs:
**      startIndex: start looking from this index
**   Outputs:
*      availableIndex: the available entry's index
**
******************************************************************************/
RETCODE PRIVATE GetNextAvailVarEntry(U32 startIndex,
                                     U32 *availableIndex)  {
   U32 lookIndex;

   if (! varTable[startIndex].used)
      *availableIndex = startIndex;
   else  {
      lookIndex = startIndex;
      while((lookIndex < currVarTableSize) &&
            (varTable[lookIndex].used)) lookIndex++;
      if ((lookIndex < currVarTableSize) && (! varTable[lookIndex].used))
         *availableIndex = lookIndex;
      else  {
         lookIndex = 0;
         while((lookIndex < startIndex) &&
               (varTable[lookIndex].used)) lookIndex++;
         if ((lookIndex < startIndex) && (! varTable[lookIndex].used))
            *availableIndex = lookIndex;
         else  {
		/* TABLE IS FULL!! Try to grow it */
            if ((hVarTable = GlobalReAlloc(hVarTable,(currVarTableSize +
                  NUM_VARTABLE_INCREMENT) * sizeof(VS_VAR_ENTRY_TYPE),
                  VARTABLE_WFLAGS)) == NULL)
               return ER_OUT_OF_MEMORY;
            if ((varTable = (VS_VAR_TABLE_TYPE)GlobalLock(hVarTable)) == NULL)
               return ER_WINDOWS_MEMLOCK;
            *availableIndex = currVarTableSize;
            currVarTableSize += NUM_VARTABLE_INCREMENT;
         }
      }
   }
   return GOOD;
}

/*************************************************************************
**
**  GetNextToken :
**
**  Used for composite variable strings.  Move the pointer to the next
**  token string and return a copy of the new token string.
**
***************************************************************************/
VOID PRIVATE GetNextToken(STRING *tokenPtr,
                          STRING token)  {

   while ((**tokenPtr != '.') &&
          (**tokenPtr != '-') &&
          (**tokenPtr != '\0')) (*tokenPtr)++;
   if (**tokenPtr != '\0') {
      if (**tokenPtr == '.')
         (*tokenPtr)++;            /* jump over the . */
      if (**tokenPtr == '-')
         (*tokenPtr) += 2;         /* jump over the -> */
   }
   GetSameToken(*tokenPtr,token);
}

/*************************************************************************
**
**  GetSameToken :
**
**  Used for composite variable strings.  Return a copy of the
**  current token string.
**
***************************************************************************/
VOID PRIVATE GetSameToken(STRING tokenPtr,
                          STRING token)  {

   LPSTR ptr;


   ptr = (LPSTR)tokenPtr;
   while ((*ptr != '.') &&
          (*ptr != '-') &&
          (*ptr != '\0'))  {
      *token = *ptr;
      ++token;
      ++ptr;
   }
   *token = '\0';
}

/**********************************************************************
**
** GetSorUComponents :
**
**  Retrieve the components of a structure or union type.
**
**  Parameters:
**      CompPtr (in/out) : reset provided component ptr to the
**                         new component parts
**      memberCount (in) :  member count of root variable/ number of
**                          components to retreive
**      typeIndex (in) :  typeIndex of root var
**
*********************************************************************/
RETCODE PRIVATE GetSorUComponents(VS_COMPONENT_TYPE **CompPtr,
                                    U16 memberCount,
                                    TYPE_INDEX typeIndex)  {
   RETCODE error;
   VS_COMPONENT_TYPE *component, *last;
   U16 bitOffset = 0;
   BOOLEAN prevCompBitfield = FALSE;
   LOOP_VAR i;

   /*
   **  It would be possible to allocate once with enough room for
   **  all components.  In fact this may help performance and
   **  reduce segmentation of available memory.
   */
   for (i = 0; i < memberCount; i++)  {
      if ((component = (VS_COMPONENT_TYPE FAR *)TMalloc(sizeof(
            *component))) == NULL)
         return ER_OUT_OF_MEMORY;
      memset(component,'\0',sizeof(*component));
      component->typeInfo.th.typeName = (LPSTR)component->typeInfo.typeNameStr;
      component->varName = (LPSTR)component->varNameStr;
      if ((error = SymGetTypeStructUnionNth(typeIndex,i,
            (TYPE_S_U_STRUCT *)component)) != GOOD) {
         TFree((LPSTR)component);
         return error;
      }
      if ((error = SymGetTypeHeader(component->typeIndex,(TYPE_HEADER_TYPE *)
            &component->typeInfo)) != GOOD) {
         TFree((LPSTR)component);
         return error;
      }  
      component->typeInfo.typeIndex = component->typeIndex;


      /* hook into the component list */
      if (*CompPtr == NULL)  {
         *CompPtr = component;
         last = component;
      }
      else  {
         last->next = component;
         last = component;
      }
      if (component->typeInfo.th.typeChoice == COMPLEX_TYPE_CLASS) {
         switch (component->typeInfo.th.t.complexType)  {
            case TY_SMALL_PTR :
            case TY_LARGE_PTR :
            case TY_16_16_PTR :
            case TY_16_32_PTR :
               if ((error = GetBaseTypeForPointer(&component->typeInfo))
                      != GOOD)
                  return error;
               prevCompBitfield = FALSE;
               break;
            case TY_C_ARRAY :
               if ((error = GetBaseTypeForArray(&component->typeInfo))
                     != GOOD)
                  return error;
               prevCompBitfield = FALSE;
               break;
            case TY_TYPE:
               if ((error = GetBaseTypeForTypedef(&component->typeInfo))
                     != GOOD)
                  return error;
               prevCompBitfield = FALSE;
               break;
            case TY_BITFIELD:
               if ((error = GetBitfieldOffsetAdjustment(&component->typeInfo,
                     prevCompBitfield,
                     &bitOffset,
                     &component->offset,
                     &component->bitOffset)) != GOOD)
                  return error;
               prevCompBitfield = TRUE;
               break;
            default:
               prevCompBitfield = FALSE;
               break;
         }
      }
   }
   return GOOD;
}

/*************************************************************************
**
**  GetTypeSignature :
**
**************************************************************************/
RETCODE PRIVATE GetTypeSignature(VS_VAR_ENTRY_TYPE *var)  {

   TYPE_SUBHEADER_TYPE *typeInfo;
   RETCODE error;

   typeInfo = var->typeInfo.next;
   while ((typeInfo != NULL) && (typeInfo->next != NULL))
      typeInfo = typeInfo->next;
   if ((error = SymGetTypeMemberCount(typeInfo->typeIndex,
         &(var->baseTypeMemberCount))) != GOOD)
      return error;
   if (typeInfo->th.typeChoice == SIMPLE_TYPE_CLASS)
      var->compListPtr = NULL;
   else {
      switch (typeInfo->th.t.complexType) {
         case TY_STRUCT:
         case TY_UNION:
            if ((error = GetSorUComponents(&var->compListPtr,
                  var->baseTypeMemberCount,typeInfo->typeIndex)) != GOOD)
               return error;
            break;
         default:
            break;
      }
   }
   return GOOD;
}
/***************************************************************************
**
**  GetVSBuf :
**
**  This routine allocates a buffer of the requested size.  It uses
**  TMalloc first and if that fails then trys GlobalAlloc.  The
**  allocation method is stored in the first word of the buffer.
**  Note:  This routine does not unlock global allocated memory.
**
**  Inputs:
**      size : size requested in bytes
**
**  Outputs:
**      bufptr : pointer to the allocated buffer
**
***************************************************************************/
RETCODE PRIVATE GetVSBuf(U32 size,
                         LPWORD *bufptr)   {
   HANDLE bufHandle;

   if ((*bufptr = (LPWORD)TMalloc(size)) == NULL)   {
      if ((bufHandle = GlobalAlloc(VARTABLE_WFLAGS,size)) == NULL)
         return ER_OUT_OF_MEMORY;
      if ((*bufptr = (LPWORD)GlobalLock(bufHandle)) == NULL)
         return ER_WINDOWS_MEMLOCK;
      *(*bufptr) = bufHandle;
   }
   else
      *(*bufptr) = VS_TMALLOC;
   return GOOD;
}

/*****************************************************************************
**
**  GetVSdisplayBuf :
**
**  This routine gets a buffer of the appropriate size, and fills
**  all the fields including the display text.
**
**  Inputs:
**      VSVarId : variable id
**      bufferNum : the buffer number to get
**      frameNum : for stack vars the associated frame.
**
**  Outputs:
**      dispPtr : pointer to the display buffer
**
******************************************************************************/
RETCODE GetVSdispBuf(DESCRIPTOR varId,
                     DESCRIPTOR bufferNum,
                     U8 frameNum,
                     VS_DISPLAY_PTR FAR *dispPtr)   {
   U32 numLines=1,
       numLinesThisBuf=1,
       lastBuf=0,
       textSize=VS_MAX_LINE_CHARS,
       startLine=0;
   STRING textptr;
   RETCODE retcode = GOOD;
   LPWORD bufptr;

   if (varTable[varId].active) {
      numLines = LinesForVar(varId);
      if (varTable[varId].errorCode)
         numLines++;                        /* add line for warning message */
      lastBuf = BuffersForVar(varId)-1;     /* buffers are 0..n */
      startLine = bufferNum * VS_MAX_BUFFER_LINES;
      numLinesThisBuf = ((numLines - startLine) > VS_MAX_BUFFER_LINES) ?
                       VS_MAX_BUFFER_LINES : (numLines - startLine);
      textSize = VS_MAX_LINE_CHARS * numLinesThisBuf;
   }
   if ((retcode = GetVSBuf(textSize+DISPLAY_BUF_OVERHEAD,&bufptr)) != GOOD)
      return retcode;
   *dispPtr = (VS_DISPLAY_BUFFER_TYPE *)bufptr;
   (*dispPtr)->bufnum = bufferNum;
   (*dispPtr)->continued = ((numLines > VS_MAX_BUFFER_LINES) &&
                         (bufferNum != lastBuf)) ? TRUE:FALSE;
   textptr = (*dispPtr)->text;
   memset(textptr,'\0',textSize);
   retcode = GetFormattedType(varId,&textptr,numLinesThisBuf,
                              startLine,bufferNum,frameNum);
   (*dispPtr)->textLength = textptr - (*dispPtr)->text;

   return retcode;
}

/*****************************************************************************
**
**  GetVarSymbolInfo :
**
**  Get the variable information from the symbol server and store it.
**
***************************************************************************/
RETCODE PRIVATE GetVarSymbolInfo(U32 tableIndex,
                                 VS_VAR_SELECTION_TYPE *varInfo,
                                 BOOLEAN interActive)   {
   VS_STRING_TYPE varName;
   STRING stringPtr;
   TYPE_INDEX typeIndex;
   VAR_STORAGE_CLASS storageClass;
   VAR_REGISTER_CLASS registerClass;
   SYM_DESCRIPTOR modDesc,funcDesc,parentDesc;
   GET_VAR_ADDR_STRUCT addr;
   BOOLEAN isConst;
   U16 bitOffset = 0;
   U32 byteOffset = 0L;
   RETCODE error;

   memset((LPSTR)varName,'\0',sizeof(varName));
   addr.getFixedAddrDesc = NULL;
   switch (varInfo->selection) {
      case SYMBOL_TAG :
         if ((error = AdrCreateAddress(&addr.getFixedAddrDesc)) != GOOD)
            return error;
         if((error = SymGetVar(varInfo->var.symbol,(LPSTR)varName,
               &typeIndex,&storageClass,&registerClass,&isConst,
               &funcDesc,&modDesc,&parentDesc,&addr))
               != GOOD) {
            AdrDestroyAddress(addr.getFixedAddrDesc);
            return error;
         }
         if ((error = StoreVarInfo(tableIndex,varInfo,(LPSTR)varName,
               typeIndex,FALSE,FALSE,storageClass,registerClass,isConst,modDesc,
               funcDesc,parentDesc,&addr,byteOffset,bitOffset,
               interActive)) != GOOD)
            return error;
         break;

      case COMPONENT_TAG :  {
         VS_COMPONENT_INFO_TYPE *compSym;
         VS_VAR_ENTRY_TYPE *parentVar;
         BOOLEAN dereferencedPtr = FALSE;
         BOOLEAN typeDereferencedPtr = FALSE;
         U8 nameString[VS_MAX_STRING];
         VAR_STORAGE_CLASS storageClass;
         VAR_REGISTER_CLASS registerClass;
         ARRAY_INDEX_NODE *arrayIndices;
         DESCRIPTOR parentId;

         memset(&nameString[0],'\0',VS_MAX_STRING);
         compSym = &varInfo->var.component;
         parentVar = &varTable[compSym->rootVarId&VS_VARID_MASK];
         parentId = compSym->rootVarId;
         if ((error = EvaluateComponentVar(parentVar,compSym,&typeIndex,
               nameString,&arrayIndices,&dereferencedPtr,&typeDereferencedPtr,
               &storageClass,&addr,&byteOffset,&bitOffset)) != GOOD)
            return error;
         MakeCompositeVarName((LPSTR)parentVar->varName,&parentVar->typeInfo,
               parentVar->dereferencedPtr,(LPSTR)nameString,
               arrayIndices,(LPSTR)varName);
         if ((parentVar->dereferencedPtr) && 
             (parentVar->varInfo.selection == COMPONENT_TAG) &&
              dereferencedPtr) {
            // save the root var id which can be different than
            // the parent var id.   
            memcpy(compSym,&parentVar->varInfo.var.component,sizeof(*compSym));
         }
         compSym->parentVarId = parentId;
         if ((parentVar->registerClass == LIVING_REG) &&
            (parentVar->storageClass == AUTO_VAR_CLASS))
            /*
            ** MRI designates unused auto vars to the D0 register
            ** either as living. In this case the child of
            ** the root is also D0 designated.
            */
            registerClass = parentVar->registerClass;
         else
            registerClass = NOT_REG;
         if ((error = StoreVarInfo(tableIndex,varInfo,(LPSTR)varName,
               typeIndex,dereferencedPtr,typeDereferencedPtr,
               storageClass,registerClass,FALSE,
               parentVar->context.module,parentVar->context.function,
               parentVar->context.lowestLevel,&addr,byteOffset,bitOffset,
               interActive)) != GOOD)
            return error;
         break;
      }
      case COMPOSITE_TAG: {
         BOOLEAN dereferencedPtr = FALSE;
         U32 attribute,pushMask;
         U8 argCount,level;
         VS_STRING_TYPE typeName,
                        fatherName;
         TYPE_HEADER_TYPE typeHeader;
         /*
         **  consists of structs, unions, ptrs, built in types,
         **  and arrays.  The kit and kaboodle.
         */
         if ((error = AdrCreateAddress(&addr.getFixedAddrDesc)) != GOOD)
            return error;
         if((error = SymGetVar(varInfo->var.composite.rootSymbol,
               (LPSTR)varName,&typeIndex,&storageClass,&registerClass,
               &isConst,&funcDesc,&modDesc,&parentDesc,&addr)) != GOOD) {
            AdrDestroyAddress(addr.getFixedAddrDesc);
            return error;
         }
         lstrcpy((LPSTR)varName,varInfo->var.composite.varString);
         stringPtr = varName;
         if (parentDesc == funcDesc) {
            typeHeader.typeName = (LPSTR)typeName;
            if ((error = SymGetFuncType(funcDesc,&attribute,
                  &varTable[tableIndex].frameType,&pushMask,&typeHeader,
                  &argCount,&level,(LPSTR)fatherName) ) != GOOD)
               return error;
         }
         if ((error = WalkVariableType(&typeIndex,varTable[tableIndex].
               frameType,&addr,&bitOffset,&byteOffset,&stringPtr,&storageClass,
               &registerClass,&dereferencedPtr))
               != GOOD)
            return error;
         if ((error = StoreVarInfo(tableIndex,varInfo,
               (LPSTR)(varName+varInfo->var.composite.selectedVarOffset),
               typeIndex,dereferencedPtr,FALSE,storageClass,registerClass,FALSE,
               modDesc,funcDesc,parentDesc,&addr,byteOffset,bitOffset,
               interActive)) != GOOD)
            return error;

         break;
      }
                                  
   }
   return GOOD;
}

/**************************************************************************
**
**  InitServer
**
****************************************************************************/
RETCODE EXPORT InitServer() {

   return (VSInit());
}

/**************************************************************************
**
**   LinesForVar :
**
**************************************************************************/
U32 PRIVATE LinesForVar(DESCRIPTOR varId)  {
   TYPE_SUBHEADER_TYPE *baseTypeInfo,
                       *typedefTypeInfo;

   if (varTable[varId].typeInfo.th.typeChoice == SIMPLE_TYPE_CLASS)
      return varTable[varId].memberCount;
   switch (varTable[varId].typeInfo.th.t.complexType)  {
      case TY_C_ARRAY :
         return(ArrayLineLimit(&varTable[varId].typeInfo));
      case TY_STRUCT :
      case TY_UNION :
         return(varTable[varId].memberCount + HEADER_AND_FOOTER);
      case TY_SMALL_PTR :
      case TY_LARGE_PTR :
      case TY_16_16_PTR :
      case TY_16_32_PTR :
      case TY_BITFIELD :
         return varTable[varId].memberCount;
      case TY_ENUM_C :
      case TY_VOID :         
         // 05/15/95 - Nghia
         // Added TY_VOID to LinesForVar() to return 1 line for
         // a TY_VOID variable.
         return (1);
      case TY_TYPE:
         CheckForTypedef(&varTable[varId].typeInfo,&typedefTypeInfo,
               &baseTypeInfo);
         if (baseTypeInfo->th.typeChoice == SIMPLE_TYPE_CLASS)
            return varTable[varId].baseTypeMemberCount;
         switch (baseTypeInfo->th.t.complexType)  {
            case TY_C_ARRAY :
               return(ArrayLineLimit(baseTypeInfo));
            case TY_STRUCT :
            case TY_UNION :
               return(varTable[varId].baseTypeMemberCount +
                     HEADER_AND_FOOTER);
            case TY_SMALL_PTR :
            case TY_LARGE_PTR :
            case TY_16_16_PTR :
            case TY_16_32_PTR :
            case TY_BITFIELD :
               return varTable[varId].baseTypeMemberCount;
            case TY_ENUM_C :
            case TY_VOID :   
               // 05/15/95 - Nghia
               // Added TY_VOID to LinesForVar() to return 1 line for
               // a TY_VOID variable
               return (1);
            default:
               return (varTable[varId].baseTypeMemberCount);
         }
      default :
         return varTable[varId].memberCount;
   }
}

/****************************************************************************
**
**  OpenVariable :
**
**  This routine checks for anonymous clients and attempts to open
**  all types of variables.
**
*****************************************************************************/
RETCODE PRIVATE  OpenVariable(DESCRIPTOR VSSessionId,
                             VS_VAR_SELECTION_TYPE *varInfo,
                             BOOLEAN interActive,
                             DESCRIPTOR *VSVarId)  {
   RETCODE retcode;

// if (varTableEntryCount >= VS_MAX_VARS)
//    return ER_VS_TABLE_FULL;
   if (VSSessionId != VS_ANONYMOUS)  {
      if ((retcode = PutVariableInTable(VSSessionId,varInfo,
            interActive,VSVarId)) != GOOD)
         return retcode;
   }
   else  {
      if (!anonymousClientInSession)  {
         if ((retcode = PutVariableInTable(VSSessionId,varInfo,
               interActive,VSVarId)) != GOOD)
            return retcode;
         anonymousClientInSession = TRUE;
      }
      else            /* anonymous client already active */
         return ER_VS_ANONYMOUS_CLIENT_ACTIVE;
   }
   return GOOD;
}

/****************************************************************************
**
**  PutVariableInTable :
**
**  This routine assigns a variable id if one is available and fills the
**  entry in the variable table.
**
******************************************************************************/
RETCODE PRIVATE PutVariableInTable(DESCRIPTOR VSSessionId,
                                   VS_VAR_SELECTION_TYPE *varInfo,
                                   BOOLEAN interActive,
                                   DESCRIPTOR *VSVarId)  {
   RETCODE err;

   if ((err = AssignVarId(VSSessionId,VSVarId)) != GOOD)
      return err;
   currVarTableIndex = *VSVarId & VS_VARID_MASK;
   if ((err = AddTableEntry(currVarTableIndex,VSSessionId,
                            varInfo,interActive)) != GOOD)
      return err;
   return GOOD;
}
/**************************************************************************
**
**  RecreateVarTable :
**
**  Something in the symbol table has changed.  Get new variable
**  information for all variables.
**
**  Note: If this method proves to be too time consuming for the user,
**  we could try only getting information for variables in the current
**  context, and thereafter for variables changing active status.
**
**************************************************************************/
VOID PRIVATE RecreateVarTable(VOID)  {

   LOOP_VAR i;
   VS_VAR_SELECTION_TYPE varInfo;
   DESCRIPTOR client;
   BOOLEAN mode;

   for (i=0; i<currVarTableSize; i++)  {
      if (varTable[i].used) {
         memmove(&varInfo,&varTable[i].varInfo,sizeof(varInfo));
         client = varTable[i].clientId;
         mode = varTable[i].interActive;
         DelTableEntry(client,(i|(client << 24)));
         AddTableEntry(i,client,&varInfo,mode);
      }
   }
}

/*****************************************************************************
**
**  StoreVarInfo :
**
**  Store the information at the variable's index of the variable table.
**
****************************************************************************/
RETCODE PRIVATE StoreVarInfo(U32 tableIndex,
                          VS_VAR_SELECTION_TYPE *varInfo,
                          LPSTR varName,
                          TYPE_INDEX typeIndex,
                          BOOLEAN dereferencedPtr,
                          BOOLEAN typeDereferencedPtr,
                          VAR_STORAGE_CLASS storageClass,
                          VAR_REGISTER_CLASS registerClass,
                          BOOLEAN isConst,
                          SYM_DESCRIPTOR modDescriptor,
                          SYM_DESCRIPTOR funcDescriptor,
                          SYM_DESCRIPTOR parentDescriptor,
                          GET_VAR_ADDR_STRUCT *addr,
                          U32 byteOffset,
                          U16 bitOffset,
                          BOOLEAN interActive)   {
   U32 attribute,pushMask;
   U8 argCount,level;
   VS_STRING_TYPE typeName,
                  fatherName;
   TYPE_HEADER_TYPE typeHeader;
   RETCODE retcode = GOOD;

   /*
   ** clean out the table entry to avoid mixing
   ** old and new data together
   */
   typeHeader.typeName = (LPSTR)typeName;
   memset(&varTable[tableIndex],'\0',sizeof(varTable[tableIndex]));
   switch (varInfo->selection) {
      case SYMBOL_TAG:
         varTable[tableIndex].symDescriptor = varInfo->var.symbol;
         varTable[tableIndex].rootVarId = NOROOT;
         varTable[tableIndex].parentVarId = NOROOT;
         break;
      case COMPOSITE_TAG:
         varTable[tableIndex].symDescriptor =
                  varInfo->var.composite.rootSymbol;
         varTable[tableIndex].rootVarId = NOROOT;
         varTable[tableIndex].parentVarId = NOROOT;
         break;
      case COMPONENT_TAG:
         varTable[tableIndex].symDescriptor = 0;
         varTable[tableIndex].rootVarId = varInfo->var.component.rootVarId;
         varTable[tableIndex].parentVarId = varInfo->var.component.parentVarId;
         break;
   }
   lstrcpy((LPSTR)&varTable[tableIndex].varName,varName);
   memmove(&varTable[tableIndex].varInfo,varInfo,sizeof(*varInfo));
   varTable[tableIndex].dereferencedPtr = dereferencedPtr;
   varTable[tableIndex].storageClass = storageClass;
   varTable[tableIndex].registerClass = registerClass;
   memmove(&varTable[tableIndex].address,addr,sizeof(*addr));
   varTable[tableIndex].invalidAddress = FALSE;
   varTable[tableIndex].interActive = interActive;
   varTable[tableIndex].context.module = modDescriptor;
   varTable[tableIndex].context.function = funcDescriptor;
   varTable[tableIndex].context.lowestLevel = parentDescriptor;
   if (allVarActiveFlag)
      varTable[tableIndex].active = TRUE;
   else
      varTable[tableIndex].active = VarIsActive(tableIndex,NULL);
   varTable[tableIndex].readOnly = isConst;
   varTable[tableIndex].typeInfo.th.typeName = (LPSTR)varTable[tableIndex].
         typeInfo.typeNameStr;
   if ((retcode = SymGetTypeHeader(typeIndex,(TYPE_HEADER_TYPE *)
         &varTable[tableIndex].typeInfo)) != GOOD)
      return retcode;
   varTable[tableIndex].typeInfo.typeIndex = typeIndex;
   varTable[tableIndex].typeInfo.byteOffset = byteOffset;
   varTable[tableIndex].typeInfo.typeDereferencedPtr = typeDereferencedPtr;

   // 05/15/95 - Nghia
   // Need to return error when the <retcode> != GOOD
   if ((retcode = SymGetTypeMemberCount(typeIndex,&varTable[tableIndex].
         memberCount)) != GOOD)
      return retcode;
   if (parentDescriptor == funcDescriptor) {
      retcode = SymGetFuncType(funcDescriptor,&attribute,
            &varTable[tableIndex].frameType,&pushMask,&typeHeader,
            &argCount,&level,(LPSTR)fatherName);
   }
   if (varTable[tableIndex].typeInfo.th.typeChoice == SIMPLE_TYPE_CLASS)  {
      varTable[tableIndex].compListPtr = NULL;
   }
   else  {
      switch (varTable[tableIndex].typeInfo.th.t.complexType)  {
         case TY_STRUCT:
         case TY_UNION:
            retcode = GetSorUComponents(&varTable[tableIndex].compListPtr,
                  varTable[tableIndex].memberCount,typeIndex);
            break;
         case TY_SMALL_PTR:
         case TY_LARGE_PTR:
         case TY_16_16_PTR:
         case TY_16_32_PTR:
            if ((retcode = GetBaseTypeForPointer(&varTable[tableIndex].
                  typeInfo)) != GOOD)
               return retcode;
            retcode = GetTypeSignature(&varTable[tableIndex]);
            break;
         case TY_C_ARRAY:
            if ((retcode = GetBaseTypeForArray(&varTable[tableIndex].
                  typeInfo)) != GOOD)
               return retcode;
            retcode = GetTypeSignature(&varTable[tableIndex]);
            break;
         case TY_TYPE:
            if ((retcode = GetBaseTypeForTypedef(&varTable[tableIndex].
                  typeInfo)) != GOOD)
               return retcode;
            retcode = GetTypeSignature(&varTable[tableIndex]);
            break;
         case TY_BITFIELD:
            if ((retcode = GetBaseTypeForBitfield(&varTable[tableIndex].
                  typeInfo)) != GOOD)
               return retcode;
            varTable[tableIndex].typeInfo.bitfieldInfo.bitOffset
                  = bitOffset;
            break;
         case TY_ENUM_C:
         case TY_UNKNOWN:
         case TY_FUNC_NODEP:
         case TY_FUNC_DEP:
         case TY_VOID:   
            // 05/15/95 - Nghia
            // Added TY_VOID to StoreVarInfo()
            break;
      }

   }
   return retcode;
}

/******************************************************************************
**
**   TerminateServer
**
*****************************************************************************/
RETCODE EXPORT TerminateServer() {

   return GOOD;
}

/*************************************************************************
**
**  UpdateVarTable
**
**  Search the variable table comparing contexts.  Set variables to be
**  active or not active depending on the context.
**
*************************************************************************/
RETCODE PRIVATE UpdateVarTable(BOOLEAN *varTableChange)  {

   LOOP_VAR i;
   CONTEXT_TYPE context;
   RETCODE error;
   
   if ((error = StkGetCurrentModule(&context.module)) != GOOD)
       return error;
   if ((error = StkGetCurrentFunction(&context.function)) != GOOD)
       return error;
   if ((error = StkGetCurrentLowestLevel(&context.lowestLevel,
      &context.symOffset)) != GOOD)
       return error;
   if ((error = StkGetCurrentLinenum(&context.currentLinenum,
          &context.currentColumn,&context.linenumIndex)) != GOOD)
       return error;
   if ((error = CpuGetPC(&context.pcAddrDesc)) != GOOD)
       return error;
   for (i=0; i<currVarTableSize; i++)  {
      if (varTable[i].used) {
         if( varTable[i].registerClass != NOT_REG ) {
            // If locked or living register then need to update
            // the current lifetimeState information for the current PC
            // since it might be alive now...
            VAR_LIFETIME_STATE *lifetimeStatePtr=NULL;
            if( varTable[i].registerClass == LIVING_REG ) 
               lifetimeStatePtr =
                  &varTable[i].address.addrData.getRegisterIndex.lifetimeState;
            else if( varTable[i].registerClass == LOCKED_REG ) 
               lifetimeStatePtr =
                  &varTable[i].address.addrData.getLockedReg.lifetimeState;
            if( lifetimeStatePtr &&
                ((error=SymGetVarLifetimeInfo(varTable[i].symDescriptor,
                                              lifetimeStatePtr))!= GOOD) )
               return(error);
         }               
         /* we now check all variable classes since pmode can
         ** affect activity
         */
         if (VarIsActive(i,&context)) {
            if (!varTable[i].active)  {
               varTable[i].active = TRUE;
               *varTableChange = TRUE;
            }
            else {
               /* var is still active has the value changed ?
               **  for now assume that the value has changed
               **  later for optimization we can store the previous
               **  MemRead buffer and do a quick compare.
               */
               *varTableChange = TRUE;
            }
         }
         else  {   /* does not match current context */
            if (varTable[i].active)  {
               varTable[i].active = FALSE;
               *varTableChange = TRUE;
            }
         }
      } /* if used */
   } /* for */

   if (context.pcAddrDesc != NULL)  {
      if ((error = AdrDestroyAddress(context.pcAddrDesc)) != GOOD)
         return error;
   }
   return GOOD;
}

/**********************************************************************
**
**  VarIsActive :
**
**  If variable matches the current context returns TRUE else FALSE.
**  This function consumes the context pc address descriptor.
**
*************************************************************************/
BOOLEAN PRIVATE VarIsActive(U32 tableIndex,
                            CONTEXT_TYPE *varContext)  {

   CONTEXT_TYPE currContext;
   CONTEXT_TYPE *context;
   DESCRIPTOR codeAddrRangeDesc;
   U32 funcStackSize;
   FUNC_CLASS funcClass;
   U8 funcName[MAX_SYMNAME_LENGTH];
   U32 attribute,pushMask;
   U8 frameType,argCount,level;
   TYPE_HEADER_TYPE returnType;
   ADDR_COMPARE result;
   BOOLEAN returnVal;
   ADDR_MODE addrMode,pcAddrMode;

   if (allVarActiveFlag)
      return TRUE;
   /* 03/24/95 Marilyn: We now encounter PUBLIC globals which do not
   ** have module information associated to them.  The storage
   ** classes of GLOBAL and STATIC indicate permanent memory storage
   ** and these variables can always show valid values and therefore
   ** are always considered active and not dependent on execution
   ** scope.
   */
   if ((varTable[tableIndex].storageClass == GLOBAL_VAR_CLASS) ||
      (varTable[tableIndex].storageClass == STATIC_VAR_CLASS)) {
      if (procFamily == FAMILY_68K)
         return TRUE;
      else {
         /* check pmode for intel family */
         if (AdrGetAddrMode(
               varTable[tableIndex].address.getFixedAddrDesc, 
               &addrMode) != GOOD)
            return FALSE;
         if (varContext != NULL) {
            if (AdrGetAddrMode(varContext->pcAddrDesc,
                  &pcAddrMode) != GOOD)
               return FALSE;
         }
         else {
            if (CpuGetPC(&currContext.pcAddrDesc) != GOOD)
               return FALSE;
            if (AdrGetAddrMode(currContext.pcAddrDesc,
                  &pcAddrMode) != GOOD) {
               AdrDestroyAddress(currContext.pcAddrDesc);
               return FALSE;
            }
            AdrDestroyAddress(currContext.pcAddrDesc);
         }
         if (addrMode == pcAddrMode)
            return TRUE;
         else if ( ((addrMode == MODE_PROT16) || (addrMode == MODE_PROT32)) &&
               ((pcAddrMode == MODE_PROT16) || (pcAddrMode == MODE_PROT32)) )
            return TRUE;
         else
            return FALSE;
      }
   }
   if (varContext == NULL) {
      if (StkGetCurrentModule(&currContext.module) != GOOD)
          return FALSE;
      if (StkGetCurrentFunction(&currContext.function) != GOOD)
          return FALSE;
      if (StkGetCurrentLowestLevel(&currContext.lowestLevel,
             &currContext.symOffset) != GOOD)
          return FALSE;
      if (StkGetCurrentLinenum(&currContext.currentLinenum,
             &currContext.currentColumn,&currContext.linenumIndex) != GOOD)
          return FALSE;
      if (CpuGetPC(&currContext.pcAddrDesc) != GOOD)
          return FALSE;
      context = &currContext;
   }
   else {
         context = varContext;
   }

   returnVal = FALSE;
   if ((varTable[tableIndex].context.module == context->module) &&
         (varTable[tableIndex].context.function == context->function)) {
      varTable[tableIndex].frameNum = 0; /* set for current context */
      if (varTable[tableIndex].context.function ==
            varTable[tableIndex].context.lowestLevel) {
         /*  you know it has to be a local variable in function scope */
         returnType.typeName = (LPSTR)funcName;
         if (SymGetFuncType(context->function,&attribute,&frameType,
               &pushMask,&returnType,&argCount,&level,
               (LPSTR)&funcName) != GOOD)
            goto CLEANUP;
         //  the CPU32 frame type 1 is frameless.
         //  and the CPU16 frame type 5 is frameless.
         if( (((cpu== PROC_CPU_CPU32) || (cpu== PROC_CPU_CPU32P))
                && (frameType==1))
             || ((cpu== PROC_CPU_CPU16)&&(frameType==5))) {
            /* when there is no stack frame the variable information is 
            ** immediately available, probably in registers instead of on 
            ** the stack.  NOTE:  The variables may not be displayed until
            ** a step has been taken into the function - this was a 
            ** ppr fix made at a later date.
            */
            returnVal = TRUE;
            goto CLEANUP;
         }
         /* Use AdrDuplicateAddress to preserve address information */
         if (AdrDuplicateAddress(context->pcAddrDesc, &codeAddrRangeDesc)
             != GOOD) goto CLEANUP;

         if (SymGetFunc(context->function,(LPSTR)funcName,&funcClass,
               &funcStackSize,codeAddrRangeDesc) != GOOD)  {
            AdrDestroyAddress(codeAddrRangeDesc);
            goto CLEANUP;
         }
         if (AdrCompareAddresses(codeAddrRangeDesc,context->pcAddrDesc,
               &result) != GOOD) {
            AdrDestroyAddress(codeAddrRangeDesc);
            goto CLEANUP;
         }
         AdrDestroyAddress(codeAddrRangeDesc);
         if (result == SECOND_ADDR_GREATER) {
            returnVal = TRUE;
         }
      }
      else  {   /* level lower than a function like a block */
         if (varTable[tableIndex].context.lowestLevel ==
               context->lowestLevel) {
            returnVal = TRUE;
         }
         else {
            /*
            ** nested block - check the range.
            */
            BOOLEAN isInRange;
            DESCRIPTOR blockAddrRangeDesc;
            /* Use AdrDuplicateAddress to preserve address information */
            if (AdrDuplicateAddress(context->pcAddrDesc, &blockAddrRangeDesc)
                != GOOD) goto CLEANUP;
            if (SymGetSymbolAddress(varTable[tableIndex].context.
                  lowestLevel,blockAddrRangeDesc) != GOOD)
               goto CLEANUP;
            if (AdrIsAddrInRange(context->pcAddrDesc,blockAddrRangeDesc,
                  &isInRange) != GOOD)
               goto CLEANUP;
            if (isInRange) returnVal = TRUE;
         }
      }
   }
   else
      returnVal = StkVarIsActive(varTable[tableIndex].context.module,
            varTable[tableIndex].context.function,
            varTable[tableIndex].context.lowestLevel,
            &varTable[tableIndex].frameNum);
CLEANUP:
   if (varContext == NULL) {
      if (context->pcAddrDesc != NULL)
         AdrDestroyAddress(context->pcAddrDesc);
   }

   return (returnVal);

}

/************************************************************************
**
**  WalkVariableType :
**
**  **RECURSIVE**
**  Walk through the composite variable to get to the base type.  The
**  address is calculated through successive calls to this routine.
**  Example (ptr->struct.field, a->b->c->d, struct.f1.f2->a)
**
**  Parameters:
**     typeIndex (in/out) : typeIndex of root variable
**     frameType (in) : indicates local composite var
**     addr (in/out) : root variable base address/resulting composite
**                     variable address
**     bitOffset (out) : resulting bit offset (used only by bitfields)
**     byteOffset (out) : resulting byte offset from base var addr
**     varNamePtr (in) : input variable name ie a->b->c.d etc
**     storageClass (out) : resulting storage class of composite var
**     registerClass (out) : resulting register class of composite
**                           var
**     dereferencedPtr (out) : resulting composite var is dereferenced
**
**************************************************************************/
RETCODE PRIVATE  WalkVariableType(TYPE_INDEX *typeIndex,
                                  U8 frameType,
                                  GET_VAR_ADDR_STRUCT *addr,
                                  U16 *bitOffset,
                                  U32 *byteOffset,
                                  STRING *varNamePtr,
                                  VAR_STORAGE_CLASS *storageClass,
                                  VAR_REGISTER_CLASS *registerClass,
                                  BOOLEAN *dereferencedPtr) {

   TYPE_HEADER_TYPE subTypeInfo;
   TYPE_SUBHEADER_TYPE typeInfo;
   VS_STRING_TYPE token,typeName;
   GET_VAR_ADDR_STRUCT newAddr;
   TYPE_INDEX pointerTypeIndex,
              typeTypeIndex = 0;
   VS_COMPONENT_TYPE compInfo;
   U16 compIndex;
   BOOLEAN noMatch;
   RETCODE error;

   *bitOffset = 0;
   *byteOffset = 0L;
   memset(&compInfo,'\0',sizeof(compInfo));
   memset(&subTypeInfo,'\0',sizeof(subTypeInfo));
   memset(&typeInfo,'\0',sizeof(typeInfo));
   compInfo.varName = (LPSTR)compInfo.varNameStr;
   compInfo.typeInfo.th.typeName = (LPSTR)compInfo.typeInfo.typeNameStr;
   typeInfo.th.typeName = (LPSTR)typeInfo.typeNameStr;
   subTypeInfo.typeName = (LPSTR)typeName;
   if ((error = SymGetTypeHeader(*typeIndex,&typeInfo.th)) != GOOD)
      return error;
   typeInfo.typeIndex = *typeIndex;
   while (typeInfo.th.t.complexType == TY_TYPE) {
      typeTypeIndex = *typeIndex;
      if ((error = SymGetTypeTypeIndex(*typeIndex,typeIndex)) != GOOD)
         return error;
      if ((error = SymGetTypeHeader(*typeIndex,&typeInfo.th)) != GOOD)
         return error;
   }
   if (typeInfo.th.typeChoice == COMPLEX_TYPE_CLASS) {
      switch (typeInfo.th.t.complexType)  {
         case TY_STRUCT :
         case TY_UNION :
            GetNextToken(varNamePtr,(STRING)&token);
            if (*token != '\0') {
               if ((error = SymGetTypeStructUnion(*typeIndex,(LPSTR)&token,
                     (TYPE_S_U_STRUCT *)&compInfo,&compIndex,
                     &noMatch)) != GOOD)
                  return error;
               /*
               **  adjust the addr by compInfo.offset but
               **  first you need to determine if it is in bytes
               **  or bits.
               */
               if ((error = SymGetTypeHeader(compInfo.typeIndex,
                     (TYPE_HEADER_TYPE *)&compInfo.typeInfo)) != GOOD)
                  return error;
               compInfo.typeInfo.typeIndex = compInfo.typeIndex;
               if ((compInfo.typeInfo.th.typeChoice == COMPLEX_TYPE_CLASS) &&
                     (compInfo.typeInfo.th.t.complexType == TY_BITFIELD)) {
                  if ((error = GetBitfield(*typeIndex, 
                                           compIndex,
                                           &compInfo)) != GOOD)
                     return error;
                  *byteOffset = compInfo.offset;
                  *bitOffset = compInfo.bitOffset;
                  // bitfields are the end of the road
                  // advance the ptr beyond last token
                  GetNextToken(varNamePtr,(STRING)&token);
               }
               if (*storageClass != AUTO_VAR_CLASS) {
                  if (compInfo.offset != 0) {
                     if ((error = AdrAddToAddress(addr->getFixedAddrDesc,
                           (U32)compInfo.offset)) != GOOD)
                        return error;
                  }
               }
               else {
                  S32 signedByteOffset = 0L;
                  signedByteOffset = (S32)compInfo.offset;
                  if (addr->addrData.getAutoVar.autoVarOffset > 0)
                     signedByteOffset = -(signedByteOffset);
                  if ((error = StkCalcStkOffset(&addr->addrData.
                        getAutoVar.autoVarOffset,signedByteOffset)) != GOOD)
                     return error;
               }
	       *typeIndex = compInfo.typeIndex;
	       if (**varNamePtr != '\0') {
                  //Hera 12/4/97 for Test Report ID 88
                  if (compInfo.typeInfo.th.t.complexType != TY_SMALL_PTR &&
                      compInfo.typeInfo.th.t.complexType != TY_LARGE_PTR &&
                      compInfo.typeInfo.th.t.complexType != TY_16_16_PTR &&
                      compInfo.typeInfo.th.t.complexType != TY_16_32_PTR)
                     return(WalkVariableType(typeIndex,frameType,addr,
                            bitOffset,byteOffset,varNamePtr,storageClass,
                            registerClass, dereferencedPtr));
	       }
            }
            break;
         case TY_SMALL_PTR :
         case TY_LARGE_PTR :
         case TY_16_16_PTR :
         case TY_16_32_PTR :
            if ((error = SymGetTypePointerTypeIndex(*typeIndex,
                  &pointerTypeIndex)) != GOOD)
               return error;
            if ((error = SymGetTypeHeader(pointerTypeIndex,&subTypeInfo))
                  != GOOD)
               return error;
            while (subTypeInfo.t.complexType == TY_TYPE) {
               typeTypeIndex = pointerTypeIndex;
               if ((error = SymGetTypeTypeIndex(pointerTypeIndex,
                     &pointerTypeIndex)) != GOOD)
                  return error;
               if ((error = SymGetTypeHeader(pointerTypeIndex,
                     &subTypeInfo)) != GOOD)
                  return error;
            }
            if ((subTypeInfo.typeChoice == COMPLEX_TYPE_CLASS) &&
                ((subTypeInfo.t.complexType != TY_STRUCT) &&
                 (subTypeInfo.t.complexType != TY_UNION))) {
               GetNextToken(varNamePtr,(STRING)&token);
            }
            else
               GetSameToken(*varNamePtr,(STRING)&token);
            if (*token != '\0') {
               /*
               **  reset the addr to where the pointer points to
               **  which is the pointer value.  Pointers can be in
               **  static memory, on the stack, or in registers.
               **
               */
               if ((error = GetPtrDereference(*registerClass,*storageClass,
                   (TYPE_SUBHEADER_TYPE *)&typeInfo, typeInfo.th.t.complexType,
                   0, 0, frameType, addr, &newAddr)) != GOOD)
                  return error;
               if ((error = AdrDestroyAddress(addr->getFixedAddrDesc))
                     != GOOD)
                  return error;
               memmove(addr,&newAddr,sizeof(*addr));
               *typeIndex = pointerTypeIndex;
               *storageClass = GLOBAL_VAR_CLASS;
               *registerClass = NOT_REG;
               *dereferencedPtr = TRUE;
               if (**varNamePtr != '\0')
                  return(WalkVariableType(typeIndex,frameType,addr,
                        bitOffset,byteOffset,varNamePtr,storageClass,
                        registerClass,dereferencedPtr));
            }
            break;
            
         case TY_BITFIELD :
            break;           /* nothing more required */
         case TY_C_ARRAY :
            break;           /* not supported first release */
         default :
            break;
      }
   }
   
   if (typeTypeIndex)
      *typeIndex = typeTypeIndex;
   return GOOD;
}

/*********************************************************************
**
**   VSInit :
**
**************************************************************************/
RETCODE EXPORT VSInit(VOID)  {
   LOOP_VAR i;
   FARPROC vsCallback;
   RETCODE error;

   if (!vsInitAlready) {
      /* varservr.dll initialization */
      if(hVarTable == NULL) { /* if table never created, do so */
         if((hVarTable = GlobalAlloc(VARTABLE_WFLAGS,
               NUM_VARTABLE_INCREMENT*sizeof(VS_VAR_ENTRY_TYPE))) == NULL)
            return ER_OUT_OF_MEMORY;
         currVarTableSize += NUM_VARTABLE_INCREMENT;
         vsCallback = MakeProcInstance((FARPROC)VSCallback,hLib);
         if ((error = EnlRegister(EVENT_STK_HALTED,
               (EVCALLBACK) vsCallback,&descEvt))!=GOOD)
            return error;
         if ((error = EnlRegister(EVENT_CPU_EDIT,              
               (EVCALLBACK) vsCallback,&descEvt))!=GOOD)
            return error;
         if ((error = EnlRegister(EVENT_CPU_PC_EDIT,       
               (EVCALLBACK) vsCallback,&descEvt))!=GOOD)
            return error;
         if ((error = EnlRegister(EVENT_SYMBOL_DELETED,
               (EVCALLBACK) vsCallback,&descEvt))!=GOOD)
            return error;
         if ((error = EnlRegister(EVENT_SYMBOL_BASE_CHANGED,
               (EVCALLBACK) vsCallback,&descEvt))!=GOOD)
            return error;
         if ((error = EnlRegister(EVENT_SYMBOL_INIT_LOAD,
               (EVCALLBACK) vsCallback,&descEvt))!=GOOD)
            return error;
         if ((error = EnlRegister(EVENT_MEM_EDIT,
               (EVCALLBACK) vsCallback,&descEvt)) != GOOD)
            return error;
      }
      if(clientTable == NULL)  {
         if ((clientTable = (VS_CLIENT_TABLE_TYPE)TMalloc(VS_MAX_CLIENTS *
               sizeof(VS_CLIENT_ENTRY_TYPE))) == NULL)
            return ER_OUT_OF_MEMORY;
         for (i=0; i < VS_MAX_CLIENTS; i++)
             clientTable[i].used = FALSE;
      }
      
      if ((error=ProcReturnCpu(&cpu))!=GOOD) return(error);
      if ((error=ProcReturnProcFamily(&procFamily))!=GOOD) return(error);
      if ((error=ProcReturnByteOrder(&procEndian))!=GOOD) return(error);
      if ((error = ProcDefaultStackSize(&procStackSize)) != GOOD) return error;
      if ((error = IniGetNumber(INI_VAR_ENTRY, INI_VAR_AUTOCALC,
                           NULL, &autoCalcBitOffset)) != GOOD)
         autoCalcBitOffset = FALSE;
      vsInitAlready = TRUE;
   }
   return GOOD;
}

/*************************************************************************
**
**  VSCallback :
**
**  Return function for event callback.
**
****************************************************************************/
RETCODE EXPORT VSCallback(U32 eventNum) {

   BOOLEAN varTableChange = FALSE;
   RETCODE error;

   // 06/02/95 - Nghia
   // Fixed PPR 10419 - Request symbol information while the loader
   // is still loading.  When EVENT_MEM_EDIT comes, the Var server
   // does not need to do any thing if there is no entry in its database.
   // This will avoid wasting processing time for nothing.
   if (varTableEntryCount == 0) return GOOD;

   switch (eventNum) {
      case EVENT_STK_HALTED:             
      case EVENT_CPU_EDIT:                  
      case EVENT_CPU_PC_EDIT: 
      case EVENT_MEM_EDIT:
         /*  Do my business first and finally propagate event */
         if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
            return ER_WINDOWS_MEMLOCK;
         if ((error = UpdateVarTable(&varTableChange)) != GOOD)
            return error;
         GlobalUnlock(hVarTable);
         if (eventNum == EVENT_STK_HALTED) {
            if ((error = EnlEventNotify(EVENT_VAR_HALTED)) != GOOD)
               return error;
         }
         else {
            if (varTableChange) {
               if ((error = EnlEventNotify(EVENT_VAR_EDIT)) != GOOD)
                  return error;
            }
         }
         break;

      case EVENT_SYMBOL_BASE_CHANGED :
         if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
            return ER_WINDOWS_MEMLOCK;
         RecreateVarTable();
         RecreateCliVarTable();
         GlobalUnlock(hVarTable);
         if ((error = EnlEventNotify(EVENT_VAR_EDIT)) != GOOD)
            return error;
         break;

      case EVENT_SYMBOL_INIT_LOAD :
      case EVENT_SYMBOL_DELETED :
         if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
            return ER_WINDOWS_MEMLOCK;
         DelVarTable();
         RecreateCliVarTable();
         GlobalUnlock(hVarTable);
         if ((error = EnlEventNotify(EVENT_VAR_EDIT)) != GOOD)
            return error;
         break;

      default :
         return GOOD;
   }
   return GOOD;
}

/*************************************************************************
**
**   VSStartSession :
**         Note: Anonymous clients (winHandle < 1) are prevented
**         from registering.
**
****************************************************************************/
RETCODE EXPORT VSStartSession(HWND winHandle,
                              DESCRIPTOR *VSSessionId,
                              LPSTR *radixMenuBufPtr)   {
   U32 availIndex;
   RETCODE retcode;

   *radixMenuBufPtr = NULL;
   if ((!winHandle) || ClientRegistered(winHandle))
      return ER_VS_CLIENT_DISCREP;
   if ((retcode = GetNextAvailClientEntry(&availIndex)) != GOOD)
      return retcode;
   clientTable[availIndex].used = TRUE;
   clientTable[availIndex].winHandle = winHandle;
   clientTable[availIndex].radixStrings = NULL;
   *VSSessionId = availIndex;
   return GOOD;
}

/******************************************************************************
**
**   VSEndSession :
**
*******************************************************************************/
RETCODE EXPORT VSEndSession(DESCRIPTOR VSSessionId)   {


   if (!ClientRegistered(clientTable[VSSessionId].winHandle))
      return ER_VS_CLIENT_DISCREP;
   clientTable[VSSessionId].used = FALSE;
   if (VSSessionId == clientEndIndex)
      for(;clientEndIndex > 1 && !clientTable[clientEndIndex].used;
           clientEndIndex--);
   if (varTable != NULL)
      CleanTable(VSSessionId);
   return GOOD;
}

/****************************************************************************
**
**  VSOpenVar :
**
******************************************************************************/
RETCODE EXPORT VSOpenVar(DESCRIPTOR VSSessionId,
                         SYM_DESCRIPTOR symbol,
                         BOOLEAN interActive,
                         DESCRIPTOR *VSVarId)   {
   RETCODE retcode = GOOD;
   VS_VAR_SELECTION_TYPE varInfo;

   if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
      return ER_WINDOWS_MEMLOCK;
   varInfo.selection = SYMBOL_TAG;
   varInfo.var.symbol = symbol;
   retcode = OpenVariable(VSSessionId,&varInfo,interActive,VSVarId);
   /*
   ** CheckErrors(&retcode);
   */
   GlobalUnlock(hVarTable);
   return retcode;
}

/****************************************************************************
**
**  VSOpenCompositeVar :
**
******************************************************************************/
RETCODE EXPORT VSOpenCompositeVar(DESCRIPTOR VSSessionId,
                                  SYM_DESCRIPTOR symbol,
                                  LPSTR varString,
                                  U16 selectedVarOffset,
                                  BOOLEAN interActive,
                                  DESCRIPTOR *VSVarId)   {
   RETCODE retcode = GOOD;
   VS_VAR_SELECTION_TYPE varInfo;

   if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
      return ER_WINDOWS_MEMLOCK;
   varInfo.selection = COMPOSITE_TAG;
   varInfo.var.composite.rootSymbol = symbol;
   varInfo.var.composite.varString = varString;
   varInfo.var.composite.selectedVarOffset = selectedVarOffset;
   retcode = OpenVariable(VSSessionId,&varInfo,interActive,VSVarId);
   /*
   ** CheckErrors(&retcode);
   */
   GlobalUnlock(hVarTable);
   return retcode;
}

/****************************************************************************
**
**  VSOpenComponentVar :
**
******************************************************************************/
RETCODE EXPORT VSOpenComponentVar(DESCRIPTOR VSSessionId,
                                  DESCRIPTOR rootVarId,
                                  U16 bufferNum,
                                  U16 lineNum,
                                  LPSTR varString,
                                  BOOLEAN interActive,
                                  DESCRIPTOR *VSVarId)   {
   RETCODE retcode = GOOD;
   VS_VAR_SELECTION_TYPE varInfo;

   if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
      return ER_WINDOWS_MEMLOCK;
   varInfo.selection = COMPONENT_TAG;
   varInfo.var.component.rootVarId = rootVarId;
   varInfo.var.component.bufferNum = bufferNum;
   varInfo.var.component.lineNum = lineNum;
   varInfo.var.component.varString = varString;
   retcode = OpenVariable(VSSessionId,&varInfo,interActive,VSVarId);
   /* 
   ** CheckErrors(&retcode);
   */
   GlobalUnlock(hVarTable);
   return retcode;
}

/****************************************************************************
**
**  VSInterpretVarAsType :
**
******************************************************************************/
RETCODE EXPORT VSInterpretVarAsType(DESCRIPTOR VSSessionId,
                                    SYM_DESCRIPTOR symbol,
                                    TYPE_INDEX typeIndex,
                                    BOOLEAN interActive,
                                    DESCRIPTOR *VSVarId)   {
   RETCODE retcode = GOOD;
   VS_VAR_SELECTION_TYPE varInfo;

   if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
      return ER_WINDOWS_MEMLOCK;
   varInfo.selection = INTERPRET_VAR_TAG;
   varInfo.var.interpVar.symbol = symbol;
   varInfo.var.interpVar.typeIndex = typeIndex;
   retcode = OpenVariable(VSSessionId,&varInfo,interActive,VSVarId);
   /* 
   ** CheckErrors(&retcode);
   */
   GlobalUnlock(hVarTable);
   return retcode;
}

/****************************************************************************
**
**  VSInterpretMemAsType :
**
******************************************************************************/
RETCODE EXPORT VSInterpretMemAsType(DESCRIPTOR VSSessionId,
                                    DESCRIPTOR memAddr,
                                    TYPE_INDEX typeIndex,
                                    BOOLEAN interActive,
                                    DESCRIPTOR *VSVarId)   {
   RETCODE retcode = GOOD;
   VS_VAR_SELECTION_TYPE varInfo;

   if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
      return ER_WINDOWS_MEMLOCK;
   varInfo.selection = INTERPRET_MEM_TAG;
   varInfo.var.interpMem.memAddr = memAddr;
   varInfo.var.interpMem.typeIndex = typeIndex;
   retcode = OpenVariable(VSSessionId,&varInfo,interActive,VSVarId);
   /* 
   ** CheckErrors(&retcode);
   */
   GlobalUnlock(hVarTable);
   return retcode;
}

/****************************************************************************
**
**  VSCloseVar :
**
******************************************************************************/
RETCODE EXPORT VSCloseVar(DESCRIPTOR VSSessionId,
                          DESCRIPTOR VSVarId)   {
   RETCODE retcode;

   if ((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable))==NULL)
       return ER_WINDOWS_MEMLOCK;
   if (VSSessionId != VS_ANONYMOUS)  {
      if ((retcode = DelTableEntry(VSSessionId,VSVarId)) != GOOD)
         return retcode;
   }
   else  {
      if (anonymousClientInSession)  {
         if ((retcode = DelTableEntry(VSSessionId,VSVarId)) != GOOD)
            return retcode;
         anonymousClientInSession = FALSE;
      }
      else            /* anonymous client not active */
         return ER_VS_CLIENT_DISCREP;
   }
   GlobalUnlock(hVarTable);
   return GOOD;
}

/*****************************************************************************
**
**  VSReadVar :
**
******************************************************************************/
RETCODE EXPORT VSReadVar(DESCRIPTOR VSSessionId,
                         DESCRIPTOR VSVarId,
                         VS_DISPLAY_PTR FAR *displayBufPtr)   {

   return(VSReadVarAtNBuf(VSSessionId,VSVarId,0,displayBufPtr));

}

/*****************************************************************************
**
**  VSReadVarAtNBuf :
**
******************************************************************************/
RETCODE EXPORT VSReadVarAtNBuf(DESCRIPTOR VSSessionId,
                               DESCRIPTOR VSVarId,
                               DESCRIPTOR bufferNum,
                               VS_DISPLAY_PTR FAR *displayBufPtr)   {
   RETCODE retcode = GOOD;

   *displayBufPtr = NULL;
   if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
      return ER_WINDOWS_MEMLOCK;
   if (((VSSessionId == VS_ANONYMOUS) && !anonymousClientInSession) ||
         (VSSessionId &&
         !ClientRegistered(clientTable[VSSessionId].winHandle)) ||
         ((VSVarId >> 24) != VSSessionId))
      return ER_VS_ACCESS_DENIED;
   VSVarId &= VS_VARID_MASK;
   if (!varTable[VSVarId].used)
      return ER_VS_VAR_NOT_FOUND;
   if (bufferNum >= BuffersForVar(VSVarId))
      return ER_VS_BUFNUM_INVALID;
   retcode = GetVSdispBuf(VSVarId,bufferNum,varTable[VSVarId].frameNum,
         displayBufPtr);
   /* 
   ** CheckErrors(&retcode);
   */
   GlobalUnlock(hVarTable);
   return retcode;
}

/****************************************************************************
**
**   VSReadStackVar
**
**   Purpose:
**      For stack variables only.
**      Given the variable id and frame number, return a pointer to the
**      buffer containing the formatted display of the variable.  Type
**      information is also encoded in the buffer.  If the variable
**      requires more than one buffer to display all the associated
**      text then the continuation flag in the display buffer is set to
**      TRUE.
**
****************************************************************************/
RETCODE EXPORT  VSReadStackVar(DESCRIPTOR VSSessionId,
                               DESCRIPTOR VSVarId,
                               DESCRIPTOR bufferNum,
                               U8 frameNum,
                               VS_DISPLAY_PTR FAR *displayBufPtr) {
   RETCODE retcode = GOOD;

   *displayBufPtr = NULL;
   if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
      return ER_WINDOWS_MEMLOCK;
   if (((VSSessionId == VS_ANONYMOUS) && !anonymousClientInSession) ||
         (VSSessionId &&
         !ClientRegistered(clientTable[VSSessionId].winHandle)) ||
         ((VSVarId >> 24) != VSSessionId))
      return ER_VS_ACCESS_DENIED;
   VSVarId &= VS_VARID_MASK;
   if (!varTable[VSVarId].used)
      return ER_VS_VAR_NOT_FOUND;
   if (bufferNum >= BuffersForVar(VSVarId))
      return ER_VS_BUFNUM_INVALID;
   /* 
   ** the variable presenter does not use the ReadStkVar function correctly
   ** and sometimes will use the routine for generic variables.  These 
   ** variables may not be active at frame 0 but could reside on the stack.
   ** Therefore, we try to be smart about what frame we are using.  The 
   ** var server knows the context information for the variable.  The only
   ** way to override this is when the presenter requests a valid stack 
   ** frame other than frame 0.  Frame 0 is current context anyway and so
   ** does not require the ReadStkVar routine. So if the requested frame is
   ** the current context, use the variable's most recent valid frame 
   ** information.
   */
   if (frameNum == 0)        /* current context */
      retcode = GetVSdispBuf(VSVarId,bufferNum,varTable[VSVarId].frameNum,
           displayBufPtr);
   else
      retcode = GetVSdispBuf(VSVarId,bufferNum,frameNum,displayBufPtr);
   /*
   ** CheckErrors(&retcode);
   */
   GlobalUnlock(hVarTable);
   return retcode;
}


/***************************************************************************
**
**  VSEditValue :
**
******************************************************************************/
RETCODE EXPORT VSEditValue(DESCRIPTOR VSSessionId,
                           DESCRIPTOR VSVarId,
                           DESCRIPTOR bufferNum,
                           U16 lineNum,
                           U16 *fieldWidth,
                           LPSTR *charSetBufPtr,
                           U16 startCol)  {
   RETCODE retcode = GOOD;

   /*
   ** if an error is returned charSetBufPtr points to
   ** replacement text
   */
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   memset(&charSetBuf,'\0',sizeof(charSetBuf));
   lstrcpy((LPSTR)&charSetBuf,*charSetBufPtr);
/* pass back pointer to global (to server) text buffer (dangerous!!) */
   *charSetBufPtr = (LPSTR)charSetBuf;
   if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
      return ER_WINDOWS_MEMLOCK;
   if (((VSSessionId == VS_ANONYMOUS) && !anonymousClientInSession) ||
         (VSSessionId &&
         !ClientRegistered(clientTable[VSSessionId].winHandle)) ||
         ((VSVarId >> 24) != VSSessionId))
      return ER_VS_ACCESS_DENIED;
   VSVarId &= VS_VARID_MASK;
   if (!varTable[VSVarId].used)
      return ER_VS_VAR_NOT_FOUND;
   if (bufferNum >= BuffersForVar(VSVarId))
      return ER_VS_BUFNUM_INVALID;
   if (lineNum >= LinesForVar(VSVarId))
      return ER_VS_LINENUM_INVALID;
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif

   /* return buffer of legal chars here */
   retcode = GetCharSetBuf(VSVarId,bufferNum,lineNum,fieldWidth,startCol);
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   /*
   ** CheckErrors(&retcode);
   */
   GlobalUnlock(hVarTable);
   return retcode;
}

/*****************************************************************************
**
**  VSNewValue :
**
******************************************************************************/
RETCODE EXPORT VSNewValue(DESCRIPTOR VSSessionId,
                          DESCRIPTOR VSVarId,
                          DESCRIPTOR bufferNum,
                          U16 lineNum,
                          U16 editFieldCharOffset,
                          LPSTR newValueText,
                          VS_DISPLAY_PTR *displayBufPtr)  {
   RETCODE retcode = GOOD;


   if((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
      return ER_WINDOWS_MEMLOCK;
   if (((VSSessionId == VS_ANONYMOUS) && !anonymousClientInSession) ||
         (VSSessionId &&
         !ClientRegistered(clientTable[VSSessionId].winHandle)) ||
         ((VSVarId >> 24) != VSSessionId))
      return ER_VS_ACCESS_DENIED;
   VSVarId &= VS_VARID_MASK;
   if (!varTable[VSVarId].used)
      return ER_VS_VAR_NOT_FOUND;
   if (bufferNum >= BuffersForVar(VSVarId))
      return ER_VS_BUFNUM_INVALID;
   if (lineNum >= LinesForVar(VSVarId))
      return ER_VS_LINENUM_INVALID;
   retcode = EditValueField(VSVarId,bufferNum,lineNum,
         editFieldCharOffset,newValueText,displayBufPtr);
   /*
   ** CheckErrors(&retcode);
   */
   GlobalUnlock(hVarTable);
   return retcode;
}


/**************************************************************************
**
**  VSVarSymbol :
**
*************************************************************************/
RETCODE EXPORT VSVarSymbol(DESCRIPTOR VSSessionId,
                           DESCRIPTOR VSVarId,
                           SYM_DESCRIPTOR *varSymbol)  {

   if ((VSVarId >> 24) != VSSessionId)
      return ER_VS_ACCESS_DENIED;
   VSVarId &= VS_VARID_MASK;
   if (!varTable[VSVarId].used)
      return ER_VS_VAR_NOT_FOUND;
   *varSymbol = varTable[VSVarId].symDescriptor;
   return GOOD;
}

/***************************************************************************
**
**   VSGetBufferSize
**
**   Purpose:
**      To retrieve the size of variable server display buffers.
**
**   Input parameters:
**      VSSessionId:  client id
**
**   Output parameters:
**      bufferSize : number of lines in a display buffer.
**
**   Error:
**      Returns GOOD if no errors.
**      Returns ER_VS_CLIENT_DISCREP if client is not registered.
**
***************************************************************************/
RETCODE EXPORT  VSGetBufferSize(DESCRIPTOR VSSessionId,
                                U8 *bufferSize) {

   if (((VSSessionId == VS_ANONYMOUS) && !anonymousClientInSession) ||
         (VSSessionId &&
         !ClientRegistered(clientTable[VSSessionId].winHandle)))
      return ER_VS_ACCESS_DENIED;
   *bufferSize = VS_MAX_BUFFER_LINES;
   return GOOD;
}

/***************************************************************************
**
**   VSGetLinesForVar
**
**   Purpose:
**      To retrieve the number of lines required to display a single var.
**
**   Input parameters:
**      VSSessionId:  client id
**      VSVarId : variable id
**
**   Output parameters:
**      numLines : number of lines required to display the var.
**
**   Error:
**      Returns GOOD if no errors.
**      Returns ER_VS_CLIENT_DISCREP if client is not registered.
**      Returns ER_VS_VAR_NOT_FOUND if variable is not found in the variable
**      table.
**      Returns ER_VS_ACCESS_DENIED if client does not have access to the
**      variable.
**
***************************************************************************/
RETCODE EXPORT  VSGetLinesForVar(DESCRIPTOR VSSessionId,
                                 DESCRIPTOR VSVarId,
                                 U32 *numLines) {

   if (((VSSessionId == VS_ANONYMOUS) && !anonymousClientInSession) ||
         (VSSessionId &&
         !ClientRegistered(clientTable[VSSessionId].winHandle)) ||
         ((VSVarId >> 24) != VSSessionId))
      return ER_VS_ACCESS_DENIED;
   VSVarId &= VS_VARID_MASK;
   if (!varTable[VSVarId].used)
      return ER_VS_VAR_NOT_FOUND;
   *numLines = LinesForVar(VSVarId);
   return GOOD;
}

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