/****************************************************************************
**
**  Name:  varcli.c
**
**  Description:
**     Entry points for CLI commands of variable server.
**
**  Status:  PRELIMINARY | CODED | REVIEWED | TESTED
**
**  $Log:   S:/tbird/arcppc/varsrv/varcli.c_v  $
** 
**    Rev 1.2   31 Mar 1998 12:02:08   Winky
** No change.
** 
**    Rev 1.1   04 Mar 1997 09:58:34   cjchen
** No change.
** 
**    Rev 1.0   17 Jan 1997 09:29:36   kevin
** Initial revision.
** 
**    Rev 1.0   03 Jun 1996 11:44:06   gene
** Initial revision.
** 
**    Rev 1.0   07 Sep 1995 11:24:54   gene
** Initial revision.
** 
**    Rev 1.19   18 Nov 1993 17:30:16   nghia
** Revised to removed compiler warning.
** 
**    Rev 1.18   16 Jul 1993 10:17:34   ernie
** Changed abortFromEsc from U16 to BOOLEAN
** 
**    Rev 1.17   05 Jul 1993 10:00:56   doug
** Use general syntax error.
** 
**    Rev 1.16   25 Jun 1993 17:57:36   paul
** Change CHECK_ABORT to TskCheckAbort
** 
**    Rev 1.15   21 Oct 1992 16:13:02   marilyn
** Fixed problem of not deleting the cli var list when a del symbols event occur
** 
**    Rev 1.14   15 Oct 1992 17:53:54   marilyn
** Fixed several bugs for beta.
** 
**    Rev 1.13   08 Aug 1992 11:56:38   tom
** CLI server registration changes.
** 
**    Rev 1.12   14 May 1992 16:32:48   marilyn
** Removed VSGetErrorText and added ToggleVar.
** 
**    Rev 1.11   13 May 1992 11:27:46   marilyn
** Added CHECK_ABORT call.
** 
**    Rev 1.10   28 Apr 1992 10:05:02   marilyn
** Empty var table message now generated for displayvar and dumpvars CLI calls.
** 
**    Rev 1.9   15 Apr 1992 10:16:46   marilyn
** Fixed bug in GetVarDescriptor when current context is unknown to use
** the module header list.
** 
**    Rev 1.8   23 Mar 1992 09:00:40   marilyn
** Updated interfaces to stack server, cpu server, and mem server.
** 
**    Rev 1.7   28 Feb 1992 10:58:50   marilyn
** No change.
** 
**    Rev 1.6   27 Jan 1992 13:46:16   marilyn
** Fixed bugs in SetVar.
** 
**    Rev 1.5   24 Jan 1992 10:18:34   brucea
** Made bug fixes to GetVarDescriptor
** 
**    Rev 1.4   23 Jan 1992 17:44:02   marilyn
** Completed coding for DisplayVar and DumpVars.
** 
**    Rev 1.3   23 Jan 1992 14:03:30   brucea
** Moved InitCli to InitCServer
** 
**    Rev 1.2   23 Jan 1992 11:53:24   marilyn
** added VSInit into Addvar for testing.
** 
**    Rev 1.1   23 Jan 1992 11:20:24   brucea
** Fixed bug in GetVarDescriptor to pass pointer to null string
** 
**    Rev 1.0   22 Jan 1992 17:40:28   marilyn
** Initial revision.
**
**  $Header:   S:/tbird/arcppc/varsrv/varcli.c_v   1.2   31 Mar 1998 12:02:08   Winky  $
**
**  Copyright (C) 1991 Microtek International.  All rights reserved.
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#ifndef _BASEWIND_
#include "basewind.h"
#endif

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

#ifndef _VARCLI_
#include "varcli.h"
#endif

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

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

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

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

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

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

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

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

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

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

/* Handle of the CLI instance */

PRIVATE HANDLE cliServerHandle;

DESCRIPTOR cliSessionId = 0;        /* Session id from variable server */

U8 cliResultsBuffer[VS_MAX_STRING];

typedef struct cliVarEntry {
   DESCRIPTOR varId;
   SYM_DESCRIPTOR varDesc;
   struct cliVarEntry *next;
} CLI_VAR_ENTRY_TYPE;

CLI_VAR_ENTRY_TYPE *cliVarList = NULL;

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

extern VS_VAR_TABLE_TYPE varTable;
extern HWND hVarTable;
extern U32  currVarTableSize;
extern BOOLEAN allVarActiveFlag;

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


RETCODE PRIVATE AddVarToCliVarList(DESCRIPTOR varId,
                                   SYM_DESCRIPTOR varDesc);

RETCODE PRIVATE DeleteVarFromCliVarList(DESCRIPTOR varId);

RETCODE PRIVATE FindVarIdForSymbol(SYM_DESCRIPTOR varDesc,
                                   DESCRIPTOR *varId);

RETCODE GetVarDescriptor(LPSTR cmdString,
                         U32 argc,
                         U32 argv[],
                         SYM_DESCRIPTOR *varDesc);

RETCODE InitCli(VOID);



RETCODE PRIVATE SendCliAllBuffersForVar(DESCRIPTOR varId);

RETCODE EXPORT SendCliResults(LPSTR results, HANDLE server);


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

/***********************************************************************
**
**   AddVarToCliVarList
**
**   Add a variable into the linked list saving the var id and symbol
**   descriptor information.
**
*************************************************************************/
RETCODE PRIVATE AddVarToCliVarList(DESCRIPTOR varId,
                                   SYM_DESCRIPTOR varDesc) {

   CLI_VAR_ENTRY_TYPE *p,*newVar;

   if ((newVar = (CLI_VAR_ENTRY_TYPE *)TMalloc(sizeof(*newVar))) == NULL)
      return ER_OUT_OF_MEMORY;
   newVar->varId = varId;
   newVar->varDesc = varDesc;
   newVar->next = NULL;
   /*
   **  add to list
   */
   if (cliVarList == NULL)  /* empty list, add first one */
      cliVarList = newVar;
   else {
      p = cliVarList;
      while (p->next != NULL)
         p = p->next;
      p->next = newVar;
   }
   return GOOD;
}


/*****************************************************************************
**
**   AddVar
**
*****************************************************************************/
RETCODE EXPORT  AddVar(LPSTR cmdString, U32 argc, U32 argv[]) {

   SYM_DESCRIPTOR varDesc;
   DESCRIPTOR varId;
   RETCODE retcode = GOOD;

   /*
   **  parse the arguments to determine the variable
   */
   if ((retcode = GetVarDescriptor(cmdString,argc,argv,&varDesc)) == GOOD) {
      /*
      **  now put variable into varTable
      */
      retcode = VSOpenVar(cliSessionId,varDesc,FALSE,&varId);
   }
   if (retcode == GOOD) {
      /*
      **  save the successful varId for the CLI
      */
      AddVarToCliVarList(varId,varDesc);
   }
   return retcode;

}  /* end of AddVar */

/***********************************************************************
**
**   DeleteVarFromCliVarList
**
**   Delete a variable from the linked list discarding the var id and
**   symbol descriptor information.
**
*************************************************************************/
RETCODE PRIVATE DeleteVarFromCliVarList(DESCRIPTOR varId) {

   CLI_VAR_ENTRY_TYPE *p,*q;
   RETCODE error;

   if (cliVarList != NULL) {
      p = cliVarList;
      q = cliVarList;
      if (p->varId == varId)  /* found it, the first one */
         cliVarList = p->next;
      else {
         p = p->next;
         while ((p != NULL) && (p->varId != varId)) {
            p = p->next;
            q = q->next;
         }
         if (p != NULL)
            q->next = p->next;
      }
      if (p != NULL) {
         if ((error = TFree((LPSTR)p)) != GOOD)
            return error;
      }
   }
   return GOOD;
}

/****************************************************************************
**
**   DeleteVar
**
****************************************************************************/
RETCODE EXPORT  DeleteVar(LPSTR cmdString, U32 argc, U32 argv[]) {

   SYM_DESCRIPTOR varDesc;
   DESCRIPTOR varId;
   RETCODE retcode = GOOD;

   /*
   **  parse the arguments to determine the variable
   */
   if ((retcode = GetVarDescriptor(cmdString,argc,argv,&varDesc)) == GOOD) {
      /*
      **  now remove variable from varTable
      */
      if ((retcode = FindVarIdForSymbol(varDesc,&varId)) == GOOD)
         retcode = VSCloseVar(cliSessionId,varId);
   }
   if (retcode == GOOD) {
      /*
      **  remove the varId for the CLI
      */
      DeleteVarFromCliVarList(varId);
   }
   return retcode;

}  /* end of DeleteVar */

/***************************************************************************
**
**   DisplayVar
**
*****************************************************************************/
RETCODE EXPORT  DisplayVar(LPSTR cmdString, U32 argc, U32 argv[]) {

   RETCODE err, retcode = GOOD;
   SYM_DESCRIPTOR varDesc;
   DESCRIPTOR varId;
   CLI_VAR_ENTRY_TYPE *p;
   BOOLEAN  abortFromEsc;


   if (cliVarList == NULL)  {
      if ((retcode = SendCliResults("CLI Variable Table Empty",
            cliServerHandle)) != GOOD)
         return retcode;
      return GOOD;
   }
   if (argc == 1) {
      /*
      **  no params; display all CLI vars
      */
      p = cliVarList;

      err = TskCheckAbort(&abortFromEsc);
      if(err!=GOOD) return err;

      while (p != NULL ) {
         err = TskCheckAbort(&abortFromEsc);
         if(err!=GOOD) return err;
         if (abortFromEsc) break;

         if ((retcode = SendCliAllBuffersForVar(p->varId)) != GOOD)
            return retcode;
         p = p->next;
      }
   }
   else {
      if ((retcode = GetVarDescriptor(cmdString,argc,argv,
            &varDesc)) == GOOD) {
         /*
         **  display one variable
         */
         if ((retcode = FindVarIdForSymbol(varDesc,&varId)) == GOOD)
            retcode = SendCliAllBuffersForVar(varId);
      }
   }
   return retcode;

}  /* end of DisplayVar */

/**************************************************************************
**
**   DumpVars
**
***************************************************************************/
#pragma argsused
RETCODE EXPORT  DumpVars(LPSTR cmdString, U32 argc, U32 argv[]) {

   RETCODE retcode, err;
   U16 unusedVars = 0;
   LOOP_VAR i;
   BOOLEAN abortFromEsc;


   if (argc > 1)
      return ER_CLI_SYNTAX;
   /*
   **  display all vars, including presenter vars
   */
   if ((varTable = (VS_VAR_ENTRY_TYPE *)GlobalLock(hVarTable)) == NULL)
      return ER_WINDOWS_MEMLOCK;


// quit from this loop if esc is pressed
   for (i = 0;(i < currVarTableSize); i++) {
      err = TskCheckAbort(&abortFromEsc);
      if(err!=GOOD) return err;
      if (abortFromEsc) break;

      if (varTable[i].used) {
         if ((retcode = SendCliAllBuffersForVar((i|(varTable[i].clientId<<24))))
               != GOOD)
            return retcode;
      }
      else
         unusedVars++;
   }

   if (unusedVars == currVarTableSize) {
      if ((retcode = SendCliResults("Variable Table Empty",
            cliServerHandle)) != GOOD)
         return retcode;
   }
   GlobalUnlock(hVarTable);
   return GOOD;

}  /* end of DumpVars */


/***********************************************************************
**
**   FindVarIdForSymbol
**
**   Find the var id for a symbol by searching the list.
**
*************************************************************************/
RETCODE PRIVATE FindVarIdForSymbol(SYM_DESCRIPTOR varDesc,
                                   DESCRIPTOR *varId) {

   CLI_VAR_ENTRY_TYPE *p;

   p = cliVarList;
   while ((p != NULL) && (p->varDesc != varDesc))
      p = p->next;
   if (p == NULL)
      return ER_VS_CLI_VAR_NOT_FOUND;
   *varId = p->varId;
   return GOOD;
}

/***************************************************************************
** GetVarDescriptor
**
** Purpose:
**    Generic routine to use the command parameters to find the symbol match
**    by "path" and name.  Uses the syntax:
**
**          [moduleName [funcName]] <varName>
**
** Input Parameters:
**    cmdString: a string of parsed tokens passed by the CLI
**    argc: number of tokens; argc-1 = number of parameters
**    argv[]: array holding an index to the parsed tokens in cmdString
**
** Output Parameters:
**    varDesc: descriptor to variable found; invalid if error produced
**
** NOT SUPPORTED:
**    module path: assumed NULL, so first module that matches name returned
**    blocks: no way to specify a block to set context; requires linenum
**
** Errors:
**    invalid module name, function name, variable name
**
***************************************************************************/
RETCODE GetVarDescriptor(LPSTR          cmdString,
                         U32            argc,
                         U32            argv[],
                         SYM_DESCRIPTOR *varDesc)  {

   SYM_DESCRIPTOR modDesc, funcDesc, symDesc;
   SYM_TYPE_TYPE symType;
   BOOLEAN isGlobal;
   RETCODE err;
   S8 nullStr[1];
   U32 symOffset;

   nullStr[0] = '\0';
   if ((argc < 2) || (argc > 4))
      return ER_CLI_SYNTAX;
   if (argc > 2) {  /* first param is module name */
      if ((err = SymGetModuleDesc((LPSTR) &cmdString[argv[1]],
                                  (LPSTR) nullStr,        /* module path */
                                  &modDesc)) != GOOD)
         return ER_VS_MODULENAME_NOT_FOUND;
   }
   symType = SYM_UNDEFINED;   /* search will include all paths */
   switch (argc) {
      case 4: /* moduleName, funcName, varName */
         if ((err = SymGetFuncByName(modDesc,
                                    (LPSTR) &cmdString[argv[2]],
                                    &funcDesc)) != GOOD)
             return ER_VS_FUNCNAME_NOT_FOUND;
         symDesc = funcDesc;  /* use this context for var search */
         break;

      case 3: /* moduleName varName */
         symDesc = modDesc; /* use module as search context */
         break;

      case 2: /* only variable name; use current context */
      /* when stack server available, remove comments from next
         line and remove call to SymGetModuleListHead */

         if ((err = StkGetCurrentLowestLevel(&symDesc,&symOffset)) != GOOD)
            return err;

         /* for now, if there is not a valid symDesc then get a valid
         ** module descriptor and call search with global variable type. 
         ** This will happen when there is no hardware and/or no context.
         */ 
         if (symDesc == NULL_SYMBOL) {
            if ((err = SymGetModuleListHead(&symDesc)) != GOOD)
               return err;  
         }

         /* force SymGetSymbolFromContext to only search global table */
         symType = SYM_GLOBAL_VAR;

         /* this code stays */
   } /* end of switch */

   /* access the last parameter of the list; use argc-1 since the array */
   /* is zero-based */
   if ((err =
      SymGetSymbolFromContext(symDesc,
                              (LPSTR) &cmdString[argv[argc-1]],
                              &symType,
                              varDesc,            /* already a pointer */
                              &isGlobal)) != GOOD)
      return ER_VS_VARNAME_NOT_FOUND;
   switch (symType) {
      case SYM_GLOBAL_VAR:       /* all the valid variable types */
      case SYM_LOCAL_VAR:
      case SYM_USER_DEFINED_VAR:
      case SYM_PUBLIC_VAR:
             return GOOD;

      default:
             return ER_VS_NAME_NOT_VARIABLE;
   }
} /* end of GetVarDescriptor */


/************************************************************************
**
**   InitCli
**
**   Register the cli with the variable server.
**
*************************************************************************/
RETCODE InitCli(VOID) {

   LPSTR dummyPtr;
   RETCODE retcode;

   if ((retcode = VSStartSession(cliServerHandle,&cliSessionId,
         &dummyPtr)) != GOOD)
      return retcode;
   return GOOD;
}

/*******************************************************************************
**
**   InitCServer :
**   Register variable server with CLI.
**
******************************************************************************/
RETCODE EXPORT InitCServer(HANDLE cliHandle, HANDLE dllHandle) {
   CSERVER_NEW_REGISTRATION FAR * msgBufPtr;

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

   msgBufPtr->stringResourceHandle = dllHandle;

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

/**************************************************************************
**
**  RecreateCliVarTable :
**
**  Something in the symbol table has changed.  Get new variable
**  information for all variables.
**
**
**  NOTE:  For now the del symbols event will clear the Cli table.
**         Later we can try to recreate it.
**
**  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.
**
**************************************************************************/
RETCODE RecreateCliVarTable(VOID)  {

   CLI_VAR_ENTRY_TYPE *p;
   BOOLEAN  abortFromEsc;
   RETCODE  err;

   p = cliVarList;
   while (p != NULL) {
      if ((err = TskCheckAbort(&abortFromEsc)) != GOOD) 
         return err;
      if (abortFromEsc) break;
      if (DeleteVarFromCliVarList(p->varId) != GOOD)
         p = p->next;
      else
         p = cliVarList;
   }
   cliVarList = NULL;
   return(GOOD);   
}

/***********************************************************************
**
**   SendCliAllBuffersForVar
**
**   Purpose:  To package the returned display buffers from var server
**   into CLI packets and send them to the CLI.  After every buffer a
**   check is done for aborting the operation.  This feature is not
**   currently supported by the CLI.
**
**   Inputs:
**      varId : var id encoded with session information.
**
************************************************************************/
RETCODE PRIVATE SendCliAllBuffersForVar(DESCRIPTOR varId) {

   LOOP_VAR i = 0;
   BOOLEAN moreBuffers = TRUE;
   VS_DISPLAY_PTR bufPtr;
   DESCRIPTOR anonymousVarId;
   RETCODE  retcode, err;
   BOOLEAN  abortFromEsc;


   while (moreBuffers) {
      err = TskCheckAbort(&abortFromEsc);
      if(err!=GOOD) return err;
      if (abortFromEsc) break;


      if ((retcode = VSReadVarAtNBuf(cliSessionId,varId,i,&bufPtr))
            != GOOD) {
         /*
         **  try to read the var as an anonymous client
         */
         if ((retcode = VSOpenVar(0,varTable[varId & VS_VARID_MASK].
               symDescriptor,FALSE,&anonymousVarId)) != GOOD)
            return retcode;
         if ((retcode = VSReadVarAtNBuf(0,anonymousVarId,i,&bufPtr))
               != GOOD)  {
            VSCloseVar(0,anonymousVarId);
            return retcode;
         }
         if ((retcode = VSCloseVar(0,anonymousVarId)) != GOOD)
            return retcode;
      }
      if ((retcode = SendCliResults((LPSTR)bufPtr->text,cliServerHandle))
            != GOOD)
         return retcode;
      if (!bufPtr->continued)
         moreBuffers = FALSE;
      FreeVSBuf(bufPtr);
      i++;
   }
   return GOOD;
}


/***************************************************************************
**
**   SendCliResults :
**
**************************************************************************/
RETCODE EXPORT SendCliResults(LPSTR results, HANDLE server) {
   CSERVER_RESULTS FAR * msgBufPtr;
   DESCRIPTOR msgBufHandle;
   U16 msgTextSize;

   msgTextSize = lstrlen(results);
   if ((msgBufHandle = GlobalAlloc(VARTABLE_WFLAGS,(sizeof(CSERVER_RESULTS) +
         msgTextSize + 2))) == NULL)
      return(ER_OUT_OF_MEMORY);
   if((msgBufPtr=(CSERVER_RESULTS *)GlobalLock(msgBufHandle)) == NULL)
      return(ER_WINDOWS_MEMLOCK);
   msgBufPtr->target = 0;
   msgBufPtr->variantCode = 0x401;
   msgBufPtr->resultTextLength = msgTextSize;
   lstrcpy(msgBufPtr->messageText,results);
   if( SendMessage(server, 0x401, msgBufHandle, 0x401L) )
      return(GOOD);
   return(ER_VS_SEND_MESSAGE_FAILED);
}

/****************************************************************************
**
**   SetVar
**
***************************************************************************/
RETCODE EXPORT  SetVar(LPSTR cmdString, U32 argc, U32 argv[]) {

   RETCODE retcode = GOOD;
   SYM_DESCRIPTOR varDesc;
   DESCRIPTOR varId;
   VS_DISPLAY_PTR bufPtr;
   S8 *p;

   if (argc < 3)
      return ER_CLI_SYNTAX;
   if ((retcode = GetVarDescriptor(cmdString,argc-1,argv,&varDesc)) == GOOD) {
      /*
      **  pass value string to var set routine, only supporting
      **  integer and char types. Verify the new value argument as
      **  integer or char type.
      */
      p = &cmdString[argv[argc-1]];
      while ((*p != NULL) && ((isxdigit(*p)) || (isascii(*p)))) p++;
      if (*p != NULL)
         return ER_CLI_SYNTAX;
      if ((retcode = FindVarIdForSymbol(varDesc,&varId)) != GOOD)
         return retcode;
      if ((retcode = VSNewValue(cliSessionId,varId,0,0,0,
            &cmdString[argv[argc-1]],&bufPtr)) != GOOD)
         return retcode;
   }
   return GOOD;

}  /* end of SetVar */

/****************************************************************************
**
**   ToggleVar
**
***************************************************************************/
#pragma argsused
RETCODE EXPORT  ToggleVar(LPSTR cmdString, U32 argc, U32 argv[]) {

   if (argc > 1)
      return ER_CLI_SYNTAX;
   if (allVarActiveFlag)
      allVarActiveFlag = FALSE;
   else
      allVarActiveFlag = TRUE;
   return GOOD;

}  /* end of ToggleVar */

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