/****************************************************************************
**
**  Name:  varservr.c
**
**  Description:
**     <description of contents and who might want to use this>
**
**  Status:  PRELIMINARY
**
**  $Log:   S:/tbird/mt2_68k/varsrv/varservr.c_v  $
** 
**    Rev 1.1   02 Jul 1998 15:08:22   hera
** ID305
** 
**    Rev 1.0   02 Jul 1998 11:56:22   hera
** Initial revision.
** 
**    Rev 1.3   18 Aug 1997 17:15:34   gene
** To fix structure pointer error.
** 
**    Rev 1.2   12 Aug 1997 11:14:06   gene
** To fix non-updated variable.
** 
**    Rev 1.1   18 Apr 1997 13:15:18   gene
** 
**    Rev 1.0   13 Feb 1997 09:08:02   gene
** Initial revision.
** 
**    Rev 1.2   26 Nov 1996 11:53:10   kevin
** 
**    Rev 1.1   26 Nov 1996 11:49:04   kevin
** modified for addrSpaceMode problem    cjchen
** 
**    Rev 1.0   07 Sep 1995 11:24:58   gene
** Initial revision.
** 
**    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_68k/varsrv/varservr.c_v   1.1   02 Jul 1998 15:08:22   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 _LDRSRV_
#include "ldrsvr.h"
#endif


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

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


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

#define DISPLAY_BUF_OVERHEAD (sizeof(VS_DISPLAY_BUFFER_TYPE))
#define BITS_PER_BYTE 8
#define NO_INDEX  0x7fff


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

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

VOID PRIVATE CheckErrors(RETCODE *retcode);

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,
                                     VAR_STORAGE_CLASS *storageClass,
                                     GET_VAR_ADDR_STRUCT *addr,
                                     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 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,
                          VAR_STORAGE_CLASS storageClass,
                          VAR_REGISTER_CLASS registerClass,
                          BOOLEAN isConst,
                          SYM_DESCRIPTOR modDescriptor,
                          SYM_DESCRIPTOR funcDescriptor,
                          SYM_DESCRIPTOR parentDescriptor,
                          GET_VAR_ADDR_STRUCT *addr,
                          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,
                                  STRING *varNamePtr,
                                  VAR_STORAGE_CLASS *storageClass,
                                  VAR_REGISTER_CLASS *registerClass,
                                  BOOLEAN *dereferencedPtr,
                                  BOOLEAN isContinue); /* <Winky 8/18/97> */

                       /****************************
                        *                          *
                        *      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->typeChoice == COMPLEX_TYPE_CLASS) {
      /* User defined type */
      if (typeInfo->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->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->typeChoice == SIMPLE_TYPE_CLASS)
            return (memberCount);
         switch (typeInfo->t.complexType) {
            case TY_STRUCT :
            case TY_UNION :
               return (memberCount + HEADER_AND_FOOTER);
            case TY_SMALL_PTR :
            case TY_LARGE_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));
}

/************************************************************************
**
**   CheckErrors :
**
*************************************************************************/
VOID PRIVATE CheckErrors(RETCODE *retcode) {

   U32 severity;

   if (*retcode == GOOD)
      return;
   severity = *retcode & E_SEVERITY_MASK;
   switch (*retcode & E_COMPONENT_MASK) {
      case MODULE_VAR :
         break;
      case MODULE_SYMBOLS :
         *retcode = ER_VS_SYMBOL_ACCESS_FAILED|severity;
         break;
      case MODULE_ADDRESS :
         *retcode = ER_VS_ADDRESS_ACCESS_FAILED|severity;
         break;
      case MODULE_STACK :
         *retcode = ER_VS_STACK_ACCESS_FAILED|severity;
         break;
      case MODULE_CPU :
         *retcode = ER_VS_CPU_ACCESS_FAILED|severity;
         break;
      case MODULE_MEMORY :
         *retcode = ER_VS_MEMORY_ACCESS_FAILED|severity;
         break;
      case MODULE_EVENT :
         *retcode = ER_VS_EVENT_ACCESS_FAILED|severity;
         break;
      case MODULE_SYSTEM :
      case MODULE_GENERIC :
         *retcode = ER_VS_SYSTEM_ACCESS_FAILED|severity;
         break;
      default :
         break;
   };
   return;
}


/**************************************************************************
**
**   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.
**
****************************************************************************/
RETCODE PRIVATE EvaluateComponentVar(VS_VAR_ENTRY_TYPE *rootVar,
                                     VS_COMPONENT_INFO_TYPE *compSym,
                                     TYPE_INDEX *typeIndex,
                                     LPU8 nameString,
                                     ARRAY_INDEX_NODE **arrayIndices,
                                     BOOLEAN *dereferencedPtr,
                                     VAR_STORAGE_CLASS *storageClass,
                                     GET_VAR_ADDR_STRUCT *addr,
                                     U16 *bitOffset) {

   TYPE_S_U_STRUCT compInfo;
   U32 byteOffset;
   U16 memberIndex = NO_INDEX;
   BOOLEAN noMatch;
   ARRAY_LIST_NODE *arrayNode;
   TYPE_SUBHEADER_TYPE *baseTypeInfo;
   TYPE_SUBHEADER_TYPE typeInfo;
   TYPE_SUBHEADER_TYPE *typeTypeInfo;
   RETCODE error;

   compInfo.name = (LPSTR)nameString;
   movmem(&rootVar->typeInfo,&typeInfo,sizeof(typeInfo));
   typeInfo.typeName = (LPSTR)typeInfo.typeNameStr;

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

   while (typeInfo.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;
   }
   switch (typeInfo.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->typeChoice == COMPLEX_TYPE_CLASS) &&
              ((baseTypeInfo->t.complexType == TY_STRUCT) ||
               (baseTypeInfo->t.complexType == TY_UNION))) {
            if (memberIndex != NO_INDEX) {
               if ((error = SymGetTypeStructUnionNth(baseTypeInfo->
                     typeIndex,memberIndex,&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->typeChoice == COMPLEX_TYPE_CLASS) &&
              ((baseTypeInfo->t.complexType == TY_C_ARRAY) ||
               (baseTypeInfo->t.complexType == TY_ENUM_C)))  {
            *typeIndex = baseTypeInfo->typeIndex;
            *storageClass = rootVar->storageClass;
            if ((error = GetComponentAddress(rootVar,typeIndex,byteOffset,
                   addr,storageClass,dereferencedPtr)) != GOOD)
               return error;
         }
         if ((baseTypeInfo->typeChoice == COMPLEX_TYPE_CLASS) &&
              ((baseTypeInfo->t.complexType == TY_SMALL_PTR) ||
               (baseTypeInfo->t.complexType == TY_LARGE_PTR))) {
            /*
            ** must be a pointer to some type
            */
            *typeIndex = baseTypeInfo->typeIndex;
            if ((error = GetComponentAddress(rootVar,typeIndex,byteOffset,
                   addr,storageClass,dereferencedPtr)) != GOOD)
               return error;
         }
         if (baseTypeInfo->typeChoice == SIMPLE_TYPE_CLASS) {
            *storageClass = rootVar->storageClass;
            *typeIndex = baseTypeInfo->typeIndex;
            if ((error = GetComponentAddress(rootVar,typeIndex,byteOffset,
                   addr,storageClass,dereferencedPtr)) != GOOD)
               return error;
         }

         break;

      case TY_STRUCT:
      case TY_UNION: {
         VS_STRING_TYPE typeName;
         TYPE_HEADER_TYPE compTypeInfo;

         compTypeInfo.typeName = (LPSTR)typeName;
         strcpy((LPSTR)nameString,(compSym->varString+1));
         nameString[(strlen((LPSTR)nameString)-1)] = '\0';
         if ((error = SymGetTypeStructUnion(*typeIndex, (LPSTR)nameString,
               &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,
               &compTypeInfo)) != GOOD)
            return error;
         if ((compTypeInfo.typeChoice == COMPLEX_TYPE_CLASS) &&
               (compTypeInfo.t.complexType == TY_BITFIELD)) {
            *bitOffset = compInfo.offset;
            compInfo.offset = *bitOffset / BITS_PER_BYTE;
            *bitOffset = *bitOffset % BITS_PER_BYTE;
         }
         byteOffset = compInfo.offset;
         *typeIndex = compInfo.typeIndex;
         *storageClass = rootVar->storageClass;
         if ((error = GetComponentAddress(rootVar,typeIndex,byteOffset,
               addr,storageClass,dereferencedPtr)) != GOOD)
            return error;
         if (rootVar->dereferencedPtr) {
            /*
            **  then the component of the root is also a dereferenced
            **  ptr regardless of its base type.  astruct->field
            */
            *dereferencedPtr = TRUE;
         }
         break;
      }
      case TY_SMALL_PTR:
      case TY_LARGE_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.size,0,rootVar->frameNum,
               rootVar->frameType,&rootVar->address,addr)) != GOOD)
            return error;
         baseTypeInfo = typeInfo.next;
         if (baseTypeInfo != NULL)
            *typeIndex = baseTypeInfo->typeIndex;
         else
            *typeIndex = typeInfo.typeIndex;
         *dereferencedPtr = 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->typeName = (LPSTR)subtype->typeNameStr;
   memset(subtype->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->typeChoice == COMPLEX_TYPE_CLASS) {
      switch (subtype->t.complexType)  {
         case TY_C_ARRAY :
            if ((error = GetBaseTypeForArray(subtype)) != GOOD)
               return error;
            break;
         case TY_SMALL_PTR :
         case TY_LARGE_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->typeName = (LPSTR)subtype->typeNameStr;
   memset(subtype->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.baseTypeIndex;
   if ((error = SymGetTypeHeader(subtype->typeIndex,
         (TYPE_HEADER_TYPE *)subtype)) != GOOD)
      return error;
   if (subtype->typeChoice == COMPLEX_TYPE_CLASS) {
      switch (subtype->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->typeName = (LPSTR)subtype->typeNameStr;
   memset(subtype->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->typeChoice == COMPLEX_TYPE_CLASS) {
      switch (subtype->t.complexType)  {
         case TY_SMALL_PTR :
         case TY_LARGE_PTR :
            if ((error = GetBaseTypeForPointer(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->typeName = (LPSTR)subtype->typeNameStr;
   memset(subtype->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->typeChoice == COMPLEX_TYPE_CLASS) {
      switch (subtype->t.complexType)  {
         case TY_SMALL_PTR :
         case TY_LARGE_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;
}
/***************************************************************************
**
**   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.
**
*********************************************************************/
RETCODE PRIVATE GetSorUComponents(VS_COMPONENT_TYPE **CompPtr,
                                    U16 memberCount,
                                    TYPE_INDEX typeIndex)  {
   RETCODE error;
   VS_COMPONENT_TYPE *component, *last;
   U16 bitfieldOffset = 0;
   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;
      component->next = NULL;
      component->typeInfo.next = NULL;
      component->typeInfo.typeName = (LPSTR)component->typeInfo.typeNameStr;
      memset(component->typeInfo.typeName,'\0',
            sizeof(component->typeInfo.typeNameStr));
      component->varName = (LPSTR)component->varNameStr;
      memset(component->varName,'\0',sizeof(component->varNameStr));
      if ((error = SymGetTypeStructUnionNth(typeIndex,i,
            (TYPE_S_U_STRUCT *)component)) != GOOD)
         return error;
      if ((error = SymGetTypeHeader(component->typeIndex,(TYPE_HEADER_TYPE *)
            &component->typeInfo)) != GOOD)
         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.typeChoice == COMPLEX_TYPE_CLASS) {
         switch (component->typeInfo.t.complexType)  {
            case TY_SMALL_PTR :
            case TY_LARGE_PTR :
               if ((error = GetBaseTypeForPointer(&component->typeInfo))
                      != GOOD)
                  return error;
               break;
            case TY_C_ARRAY :
               if ((error = GetBaseTypeForArray(&component->typeInfo))
                     != GOOD)
                  return error;
               break;
            case TY_TYPE:
               if ((error = GetBaseTypeForTypedef(&component->typeInfo))
                     != GOOD)
                  return error;
               break;
            case TY_BITFIELD:
               if ((error = GetBaseTypeForBitfield(&component->typeInfo))
                     != GOOD)
                  return error;
               bitfieldOffset = component->offset;
               component->typeInfo.bitfieldInfo.bitOffset =
                     component->offset;
               component->offset = bitfieldOffset / BITS_PER_BYTE;
               component->bitOffset = bitfieldOffset % BITS_PER_BYTE;
               break;
            default:
               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->typeChoice == SIMPLE_TYPE_CLASS)
      var->compListPtr = NULL;
   else {
      switch (typeInfo->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;
   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,storageClass,registerClass,isConst,modDesc,
               funcDesc,parentDesc,&addr,bitOffset,interActive)) != GOOD)
            return error;
         break;

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

         memset(&nameString[0],'\0',VS_MAX_STRING);
         compSym = &varInfo->var.component;
         rootVar = &varTable[compSym->rootVarId&VS_VARID_MASK];
         if ((error = EvaluateComponentVar(rootVar,compSym,&typeIndex,
               nameString,&arrayIndices,&dereferencedPtr,&storageClass,
               &addr,&bitOffset)) != GOOD)
            return error;
         MakeCompositeVarName((LPSTR)rootVar->varName,&rootVar->typeInfo,
               rootVar->dereferencedPtr,(LPSTR)nameString,
               arrayIndices,(LPSTR)varName);
         if ((rootVar->registerClass == LIVING_REG) &&
            (rootVar->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 = rootVar->registerClass;
         else
            registerClass = NOT_REG;
         if ((error = StoreVarInfo(tableIndex,varInfo,(LPSTR)varName,
               typeIndex,dereferencedPtr,storageClass,registerClass,FALSE,
               rootVar->context.module,rootVar->context.function,
               rootVar->context.lowestLevel,&addr,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,&stringPtr,&storageClass,
               &registerClass,&dereferencedPtr,TRUE)) /* <Winky 8/18/97> */
               != GOOD)
            return error;
         if ((error = StoreVarInfo(tableIndex,varInfo,
               (LPSTR)(varName+varInfo->var.composite.selectedVarOffset),
               typeIndex,dereferencedPtr,storageClass,registerClass,FALSE,
               modDesc,funcDesc,parentDesc,&addr,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.typeChoice == SIMPLE_TYPE_CLASS)
      return varTable[varId].memberCount;
   switch (varTable[varId].typeInfo.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_BITFIELD :
         return varTable[varId].memberCount;
      case TY_ENUM_C :
         return (1);
      case TY_TYPE:
         CheckForTypedef(&varTable[varId].typeInfo,&typedefTypeInfo,
               &baseTypeInfo);
         if (baseTypeInfo->typeChoice == SIMPLE_TYPE_CLASS)
            return varTable[varId].baseTypeMemberCount;
         switch (baseTypeInfo->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_BITFIELD :
               return varTable[varId].baseTypeMemberCount;
            case TY_ENUM_C :
               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 (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,
                          VAR_STORAGE_CLASS storageClass,
                          VAR_REGISTER_CLASS registerClass,
                          BOOLEAN isConst,
                          SYM_DESCRIPTOR modDescriptor,
                          SYM_DESCRIPTOR funcDescriptor,
                          SYM_DESCRIPTOR parentDescriptor,
                          GET_VAR_ADDR_STRUCT *addr,
                          U16 bitOffset,
                          BOOLEAN interActive)   {
   U32 attribute,pushMask;
   U8 argCount,level;
   VS_STRING_TYPE typeName,
                  fatherName;
   TYPE_HEADER_TYPE typeHeader;
   SPACE_MODE spaceMode,loadMode;
   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;
         break;
      case COMPOSITE_TAG:
         varTable[tableIndex].symDescriptor = varInfo->var.composite.rootSymbol;
         varTable[tableIndex].rootVarId = NOROOT;
         break;
      case COMPONENT_TAG:
         varTable[tableIndex].symDescriptor = 0;
         varTable[tableIndex].rootVarId = varInfo->var.component.rootVarId;
         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].interActive = interActive;
   varTable[tableIndex].context.module = modDescriptor;
   varTable[tableIndex].context.function = funcDescriptor;
   varTable[tableIndex].context.lowestLevel = parentDescriptor;
   /* added by cjchen 1996.11.26 */
   if ((retcode=CpuGetSpaceMode(&spaceMode))!=GOOD)
      return retcode;
   loadMode = LdrSpaceMode(); //ID247
   if (loadMode != COMMON_MODE && loadMode!=spaceMode)
      varTable[tableIndex].active=FALSE;
   else {
   /* added by cjchen EOF */
      if (allVarActiveFlag)
         varTable[tableIndex].active = TRUE;
      else
         varTable[tableIndex].active = VarIsActive(tableIndex,NULL);
   }
   varTable[tableIndex].readOnly = isConst;
   varTable[tableIndex].typeInfo.typeName = (LPSTR)varTable[tableIndex].
         typeInfo.typeNameStr;
   if ((retcode = SymGetTypeHeader(typeIndex,(TYPE_HEADER_TYPE *)
         &varTable[tableIndex].typeInfo)) != GOOD)
      return retcode;
   varTable[tableIndex].typeInfo.typeIndex = typeIndex;

   retcode = SymGetTypeMemberCount(typeIndex,&varTable[tableIndex].
         memberCount);
   if (parentDescriptor == funcDescriptor) {
      retcode = SymGetFuncType(funcDescriptor,&attribute,
            &varTable[tableIndex].frameType,&pushMask,&typeHeader,
            &argCount,&level,(LPSTR)fatherName);
   }
   if (varTable[tableIndex].typeInfo.typeChoice == SIMPLE_TYPE_CLASS)  {
      varTable[tableIndex].compListPtr = NULL;
   }
   else  {
      switch (varTable[tableIndex].typeInfo.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:
            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:
            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;
   SPACE_MODE spaceMode,loadMode;

   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);
         }               
         if ((varTable[i].dereferencedPtr) ||
               (varTable[i].storageClass != GLOBAL_VAR_CLASS)) {
            /* added by cjchen 1996.11.26 */
            if ((error=CpuGetSpaceMode(&spaceMode))!=GOOD)
               return error;
            loadMode = LdrSpaceMode(); //ID247
            if (loadMode != COMMON_MODE || loadMode==spaceMode) {
            /* added by cjchen EOF */
               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;
                  }
               }
            /* added by cjchen 1996.11.26 */
            }
            else
               varTable[i].active=FALSE;
            /* added by cjchen EOF */

         } /* if  ! GLOBAL VAR */
         if (varTable[i].storageClass == GLOBAL_VAR_CLASS) {
            /*  Global vars are always active, just check values.
            **  Assume for now that the value may have changed.
            **  Later for optimization we can store the previous
            **  MemRead buffer and do a quick compare.
            */
            *varTableChange = TRUE;
            /* added by cjchen 1996.11.26 */
            if ((error=CpuGetSpaceMode(&spaceMode))!=GOOD)
               return error;
            loadMode = LdrSpaceMode();//ID247
            if (loadMode != COMMON_MODE || loadMode==spaceMode)
               varTable[i].active=TRUE;
            else
               varTable[i].active=FALSE;
            /* added by cjchen EOF */
            /* added by Winky <8/12/97> */
            if (!VarIsActive(i,&context)) {
               if (varTable[i].active)
                  varTable[i].active=FALSE;
            }
            /* eof by Winky <8/12/97> */
         }
      } /* 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;

   if (allVarActiveFlag)
      return TRUE;
   if (((varTable[tableIndex].storageClass == GLOBAL_VAR_CLASS) &&
        (varTable[tableIndex].context.module == varTable[tableIndex].
        context.lowestLevel)) ||
      ((varTable[tableIndex].storageClass == STATIC_VAR_CLASS) &&
       (varTable[tableIndex].context.module == varTable[tableIndex].
       context.lowestLevel)))
      return TRUE;
   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 :
**
**  Walk through the composite variable to get to the base type.  The
**  address is calculated through successive calls to this routine.
**
**************************************************************************/
RETCODE PRIVATE  WalkVariableType(TYPE_INDEX *typeIndex,
                                  U8 frameType,
                                  GET_VAR_ADDR_STRUCT *addr,
                                  U16 *bitOffset,
                                  STRING *varNamePtr,
                                  VAR_STORAGE_CLASS *storageClass,
                                  VAR_REGISTER_CLASS *registerClass,
                                  BOOLEAN *dereferencedPtr,
                                  BOOLEAN isContinue) {

   TYPE_HEADER_TYPE subTypeInfo,typeInfo,compTypeInfo;
   VS_STRING_TYPE token,varName,typeName;
   GET_VAR_ADDR_STRUCT newAddr;
   TYPE_INDEX pointerTypeIndex,
              typeTypeIndex = 0;
   TYPE_S_U_STRUCT compInfo;
   U16 compIndex;
   S32 byteOffset;
   BOOLEAN noMatch;
   RETCODE error;
   /* added by <Winky 8/18/97> */
   LPSTR ptrVar;
   BOOLEAN isGo;
   /* eof <Winky 8/18/97> */
   compInfo.name = (LPSTR)varName;
   typeInfo.typeName = (LPSTR)typeName;
   compTypeInfo.typeName = (LPSTR)typeName;
   subTypeInfo.typeName = (LPSTR)typeName;
   if ((error = SymGetTypeHeader(*typeIndex,&typeInfo)) != GOOD)
      return error;
   while (typeInfo.t.complexType == TY_TYPE) {
      typeTypeIndex = *typeIndex;
      if ((error = SymGetTypeTypeIndex(*typeIndex,typeIndex)) != GOOD)
         return error;
      if ((error = SymGetTypeHeader(*typeIndex,&typeInfo)) != GOOD)
         return error;
   }
   if ((typeInfo.typeChoice == COMPLEX_TYPE_CLASS) && isContinue) { /* <Winky 8/18/97> */
      switch (typeInfo.t.complexType)  {
         case TY_STRUCT :
         case TY_UNION :
            GetNextToken(varNamePtr,(STRING)&token);
            if (*token != '\0') {
               if ((error = SymGetTypeStructUnion(*typeIndex,
                     (LPSTR)&token,&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,
                     &compTypeInfo)) != GOOD)
                  return error;
               if ((compTypeInfo.typeChoice == COMPLEX_TYPE_CLASS) &&
                     (compTypeInfo.t.complexType == TY_BITFIELD)) {
                  *bitOffset = compInfo.offset;
                  compInfo.offset = *bitOffset / BITS_PER_BYTE;
                  *bitOffset = *bitOffset % BITS_PER_BYTE;
               }
               if (*storageClass != AUTO_VAR_CLASS) {
                  if ((error = AdrAddToAddress(addr->getFixedAddrDesc,
                        (U32)compInfo.offset)) != GOOD)
                     return error;
               }
               else {
                  byteOffset = (S32)compInfo.offset;
                  if (addr->addrData.getAutoVar.autoVarOffset > 0)
                     byteOffset = -(byteOffset);
                  if ((error = StkCalcStkOffset(&addr->addrData.
                        getAutoVar.autoVarOffset,byteOffset)) != GOOD)
                     return error;
               }
               *typeIndex = compInfo.typeIndex;
               /*added by <Winky 8/18/97> */
               if (**varNamePtr != '\0') {
                  isGo = FALSE;
                  ptrVar = (LPSTR)*varNamePtr;
                  while (*ptrVar != '\0') {
                     if ((*ptrVar == '.') || (*ptrVar == '-')) isGo = TRUE;
                     ++ptrVar;
                  }
                  /* eof <Winky 8/18/97> */
                  return(WalkVariableType(typeIndex,frameType,addr,
                            bitOffset,varNamePtr,storageClass,registerClass,
                            dereferencedPtr,isGo)); /* <Winky 8/18/97> */
               }
            }
            break;
         case TY_SMALL_PTR :
         case TY_LARGE_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,
                     typeInfo.sizeInMAUs,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,varNamePtr,storageClass,registerClass,
                        dereferencedPtr,TRUE)); /* <Winky 8/18/97> */

            }
            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);
      
      vsInitAlready = TRUE;
   }
   return GOOD;
}

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

   BOOLEAN varTableChange = FALSE;
   RETCODE error;

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