// --------------------------------------------------------------------
// 
// Copyright (c) 1991 Microsoft Corporation
// 
// Module Name:
// 
//     cmd.c
// 
// Abstract:
// 
// 
// 
// Author:
// 
//     Tom Adams (tomad) 11-May-1991
// 
// Revision History:
// 
//     11-May-1991    tomad
// 
//     Created
// 
// 
//     Sanjeev Katariya (sanjeevk) 4-6-1993
//        Bug# 5203: The routine TpctlCopyAdapterAddress() needed modification to support
//                   the offset introduced by the Media type being returned on an Adapter Open.
//                   This was done in order to be able to correctly set the OID based on the
//                   medium
// 
//        Added support for commands DISABLE, ENABLE, SHELL, RECORDINGENABLE, RECORDINGDISABLE,
//        Tpctl Options w,c and ?, fixed multicast address accounting
// 
//    Tim Wynsma (timothyw)    4-27-94
//        Added performance testing
//                             5-18-94
//        Added setglobal command; cleanup
//                             6-08-94
//        Chgd perf test to client/server model
//
// ---------------------------------------------------------------------

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <windows.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "tpctl.h"
#include "parse.h"


extern CMD_CODE CommandCode[] = {

    { CMD_ERR,         "Unknown",                "U"  },
    { VERBOSE,         "Verbose",                "V"  },
    { SETENV,          "SetEnvironment",         "SE" },
    { READSCRIPT,      "ReadScript",             "RS" },
    { BEGINLOGGING,    "BeginLogging",           "BL" },
    { ENDLOGGING,      "EndLogging",             "EL" },
    { WAIT,            "Wait",                   "W"  },
    { GO,              "Go",                     "G"  },
    { PAUSE,           "Pause",                  "P"  },
    { LOAD,            "Load",                   "L"  },
    { UNLOAD,          "Unload",                 "U"  },
    { OPEN,            "Open",                   "O"  },
    { CLOSE,           "Close",                  "C"  },
    { SETPF,           "SetPacketFilter",        "SP" },
    { SETLA,           "SetLookAheadSize",       "LA" },
    { ADDMA,           "AddMulticastAddress",    "AM" },
    { DELMA,           "DeleteMulticastAddress", "DM" },
    { SETFA,           "SetFunctionalAddress",   "SF" },
    { SETGA,           "SetGroupAddress",        "SG" },
    { QUERYINFO,       "QueryInformation",       "QI" },
    { QUERYSTATS,      "QueryStatistics",        "QS" },
    { SETINFO,         "SetInformation",         "SI" },
    { RESET,           "Reset",                  "R"  },
    { SEND,            "Send",                   "S"  },
    { STOPSEND,        "StopSend",               "SS" },
    { WAITSEND,        "WaitSend",               "WT" },
    { RECEIVE,         "Receive",                "RC" },
    { STOPREC,         "StopReceive",            "SR" },
    { GETEVENTS,       "GetEvents",              "GE" },
    { STRESS,          "Stress",                 "ST" },
    { STRESSSERVER,    "StressServer",           "SV" },
    { ENDSTRESS,       "EndStress",              "ES" },
    { WAITSTRESS,      "WaitStress",             "WS" },
    { CHECKSTRESS,     "CheckStress",            "CS" },
    { BREAKPOINT,      "BreakPoint",             "BP" },
    { QUIT,            "Quit",                   "Q"  },
    { HELP,            "Help",                   "H"  },
    { SHELL,           "Shell",                  "SH" },
    { RECORDINGENABLE, "RecordingEnable",        "RE" },
    { RECORDINGDISABLE,"RecordingDisable",       "RD" },
    { DISABLE,         "Disable",                "DI" },
    { ENABLE,          "Enable",                 "EN" },
    { REGISTRY,        "Registry",               "RG" },
    { PERFSERVER,      "PerformServer",          "PS" },
    { PERFCLIENT,      "PerformClient",          "PC" },
    { SETGLOBAL,       "SetGlobalVar",           "SET"}
};


extern BOOL WriteThrough;
extern BOOL ContinueOnError;


DWORD
TpctlParseCommandLine(
    IN WORD argc,
    IN LPSTR argv[]
   )

// -----------------
// 
// Routine Description:
// 
//     This routine parses the command line arguments.  If there is a
//     script file and or log file they are loaded, and will be read from
//     when that test actually starts.  If there is an adapter to be loaded
//     it is written to the global var AdapterName and will be loaded
//     by a later routine.
// 
// Arguments:
// 
//     argc - the number of arguments passed in at startup.
// 
//     argv - the argument vector containing the arguments passed in
//            from the command line.
// 
// Return Value:
// 
//     DWORD - NO_ERROR if all the arguments are valid, and the
//             script and log file are opened and loaded correctly.
//             ERROR_INVALID_PARAMETER otherwise.
// 
// -------------------- 

{
    DWORD   Status;
    CHAR    *TmpArgv[4];
    CHAR    *TmpOptions = "/N";
    INT     i;

    //
    // Parameter validations
    //
    if ( ( argc < 1 ) || ( argc > 4 ) ) 
    {
        TpctlErrorLog("\n\tTpctl: Invalid Command Line Argument(s).\n",NULL);
        TpctlUsage();
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Read the command line arguments into the global command buffer.
    // We are going to temporarily "fake" that we are reading commands
    // from a script so the parse agruments routine will not prompt
    // the user for any additional info if all the arguments are not
    // given.  This will be disabled immediately following the call.
    //

    ScriptIndex++;

    //
    // This is very specific to this routine. The current method of parsing
    // does not lead itself well to optionals
    //
    TmpArgv[0] = TmpArgv[1] = TmpArgv[2] = TmpArgv[3] = NULL;
    for( i = 0; i < argc; i++ ) 
    {
        TmpArgv[i] = argv[i];
    }

    if ( ( argc >= 2 ) && ( argv[1][0] != '/' ) ) 
    {
        if ( argc == 4 ) 
        {
            TpctlErrorLog("\n\tTpctl: Invalid Command Line Argument(s).\n",NULL);
            TpctlUsage();
            return ERROR_INVALID_PARAMETER;
        }

        //
        // We now shift things around. At this point we now that argc must be three
        //
        argc++;
        TmpArgv[1] = TmpOptions;
        TmpArgv[2] = argv[1];
        TmpArgv[3] = argv[2];

    } 
    else 
    {
        if ( (argc >=2 ) && (argv[1][0] == '/') && 
             (strlen( argv[1] ) > (TPCTL_OPTION_SIZE-1)) ) 
        {
            TpctlErrorLog("\n\tTpctl: Invalid Command Line Argument(s).\n",NULL);
            TpctlUsage();
            return ERROR_INVALID_PARAMETER;
        }

    }

    if ( TpctlParseArguments(   CommandLineOptions,
                                Num_CommandLine_Params,
                                argc,
                                TmpArgv ) == -1 ) 
    {
        TpctlErrorLog("\n\tTpctl: Invalid Command Line Argument(s)\n",NULL);
        TpctlUsage();
        ScriptIndex--;
        return ERROR_INVALID_PARAMETER;
    }

    ScriptIndex--;

    //
    // Check the options
    //

    _strupr( GlobalCmdArgs.TpctlOptions );

    if ( strchr( GlobalCmdArgs.TpctlOptions, '?' ) != NULL ) 
    {
        TpctlUsage();
        return ERROR_INVALID_PARAMETER;
    }

    if ( strchr( GlobalCmdArgs.TpctlOptions, 'W' ) != NULL ) 
    {
        WriteThrough = FALSE;
    }

    if ( strchr( GlobalCmdArgs.TpctlOptions, 'C' ) != NULL ) 
    {
        ContinueOnError = TRUE;
    }


    //
    // If there is a script file to be opened.
    //

    if ( GlobalCmdArgs.ARGS.FILES.ScriptFile[0] != '\0' ) 
    {
        //
        // Then open it and the log file if it exists.
        //

        Status = TpctlLoadFiles(    GlobalCmdArgs.ARGS.FILES.ScriptFile,
                                    GlobalCmdArgs.ARGS.FILES.LogFile );

        if ( Status != NO_ERROR ) 
        {
            TpctlUsage();
            return Status;
        }

    //
    // Otherwise if there is only a logfile name with no script file
    // name print the usage message and return an error.
    //

    } 
    else if ( GlobalCmdArgs.ARGS.FILES.LogFile[0] != '\0' ) 
    {
        TpctlErrorLog("\n\tTpctl: Invalid Command Line Argument(s).\n",NULL);
        TpctlUsage();
        return ERROR_INVALID_PARAMETER;
    }

    return NO_ERROR;
}



VOID
TpctlUsage (
    VOID
    )

// ------------------
// 
// Routine Description:
// 
//     This routine prints out a usage statement.
// 
// Arguments:
// 
//     None
// 
// Return Value:
// 
//     None.
// 
// -- -------------

{
    printf("\n\tUSAGE: TPCTL [/[?|W|C]] [SCRIPT_FILE_NAME [LOG_FILE_NAME]]\n\n");

    printf("\tWhere:\n\n");

    printf("\tSCRIPT_FILE_NAME - is an OPTIONAL script file containing test\n");
    printf("\t                   commands.\n\n");

    printf("\tLOG_FILE_NAME    - is an OPTIONAL log file for logging test results.\n");
    printf("\t                   Defaults to TESTPROT.LOG.  A SCRIPT_FILE_NAME must\n");
    printf("\t                   precede a LOG_FILE_NAME.\n\n");

    printf("\tOPTIONS:\n\n");
    printf("\t  W              - Disables write through which speeds up TPCTL as\n");
    printf("\t                   writes to the log files are now cached. Note that\n");
    printf("\t                   this exposes the risk of the log file not being\n");
    printf("\t                   updated should the system crash during a test.\n");
    printf("\t                   WRITE_THROUGH is enabled by default.\n\n");

    printf("\t  C              - Enables TPCTL to continue on errors encountered during\n");
    printf("\t                   script testing. TPCTL will stop script processing on\n");
    printf("\t                   script errors by default.\n\n");

    printf("\t  ?              - Access command online help.\n\n");

}




VOID
TpctlHelp(
    LPSTR Command
    )

// ------------------
// 
// Routine Description:
// 
//     This routine prints out help statements for each of the supported
//     commands.
// 
// Arguments:
// 
//     Command - The command to give the help information for.  If no command
//               is given then a list of all the commands that are supported
//               will be printed.
// 
// Return Value:
// 
//     None.
// 
// -----------------

{
    DWORD CmdCode;

    if ( GlobalCmdArgs.ARGS.CmdName[0] == '\0' ) 
    {
        CmdCode = HELP;
    } 
    else 
    {
        CmdCode = TpctlGetCommandCode( Command );
    }

    printf("\n\tThe syntax of this command is:\n\n");

    switch ( CmdCode ) 
    {
        case HELP:
            printf("\tHELP [command]\n\n");
            printf("\tHelp is available on the following FUNCTIONAL commands:\n\n");
            printf("\t  (AM) AddMulticastAddress         (C) Close\n");
            printf("\t  (DM) DeleteMulticastAddress     (GE) GetEvents\n");
            printf("\t   (O) Open                       (QI) QueryInformation\n");
            printf("\t  (QS) QueryStatistics            (RC) Receive\n");
            printf("\t   (R) Reset                       (S) Send\n");
            printf("\t  (SF) SetFunctionalAddress       (SG) SetGroupAddress\n");
            printf("\t  (SI) SetInformation             (LA) SetLookAheadSize\n");
            printf("\t  (SP) SetPacketFilter            (SR) StopReceive\n");
            printf("\t  (SS) StopSend                   (WT) WaitSend\n\n");
            printf("\tHelp is available on the following STRESS commands:\n\n");
            printf("\t  (CS) CheckStress                (ES) EndStress\n");
            printf("\t  (ST) Stress                     (SV) StressServer\n");
            printf("\t  (WS) WaitStress\n\n");
            printf("\tHelp is available on the following test control commands:\n\n");
            printf("\t  (BL) BeginLogging               (BP) BreakPoint\n");
            printf("\t  (EL) EndLogging                  (G) Go\n");
            printf("\t   (H) Help                        (P) Pause\n");
            printf("\t   (Q) Quit                       (RS) ReadScript\n");
            printf("\t  (SE) SetEnvirnoment              (V) Verbose\n");
            printf("\t   (W) Wait                       (RE) RecordingEnable\n");
            printf("\t  (RD) RecordingDisable           (SH) CommandShell\n");
            printf("\t  (DI) Disable                    (EN) Enable\n");
            printf("\t  (RG) Registry\n\n");
            printf("\tThe command may be entered in either the short form or\n");
            printf("\tthe long form.  The short form is described by the letter\n");
            printf("\tor letters in the parentheses, while the long form is the\n");
            printf("\tword or phrase following.\n\n");
            break;

        case VERBOSE:
            printf("\tVERBOSE\n\n");
            printf("\tVerbose enables and disables the output of each command and its\n");
            printf("\tresults to the screen.  Errors will be printed to the screen\n");
            printf("\tregardless of the state of the Verbose flag.\n\n");
            printf("\t\"V\" - the short form of the command.\n");
            break;

        case SETENV:
            printf("\tSETENVIRONMENT [Open_Instance] [Environment_Variable]\n\n");
            printf("\tSetEnvironment allows the user to customize environment\n");
            printf("\tvariables that effect the running of tests.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to set the environment variable\n");
            printf("\t                on.  The default value is 1.\n\n");
            printf("\tEnvironment_Variable - the variable(s) to set for a given call.\n\n");
            printf("\t                       All variables are set back to their defaults\n");
            printf("\t                       on each call unless otherwise specified.\n\n");
            printf("\tEnvironment Variable values that may be set are:\n\n");
            printf("\tWindowSize - the number of packets in the windows buffer of the\n");
            printf("\t             windowing algorithm.  The default is 10 packets.\n\n");
            printf("\tRandomBuffer - the maximum value passed to the rand routine to\n");
            printf("\t               determine the number of buffers in RAND_MAKEUP packets.\n");
            printf("\t               The default value is 5 which generates an average\n");
            printf("\t               buffer size of 1/5 of the packet size.\n\n");
            printf("\tStressAddress - the multicast or functional address that will be\n");
            printf("\t                used to initialize a stress test.  All machines in\n");
            printf("\t                a given stress test must use the same StressAddress.\n\n");
            printf("\tStressDelay - the standard number of seconds to delay ecah loopp\n");
            printf("\t              through a stress test.  The default is 1/1000 seconds.\n\n");
            printf("\tUpForAirDelay - the number of seconds to delay on the loop through\n");
            printf("\t                a stress test after DelayInterval iterations have\n");
            printf("\t                occurred.  The default is 1/100 seconds.\n");
            printf("\tDelayInterval - the number of StressDelays between each UpForAirDelay\n");
            printf("\t                during a stress test.  The default is 10 iterations.\n\n");
            printf("\t\"SE\" - the short form of the command.\n");
            break;

        case READSCRIPT:
            printf("\tREADSCRIPT [Script_File] [Log_File]\n\n");
            printf("\tReadScript reads test commands from a script file, executes\n");
            printf("\tthe commands and logs the results of the command to a log file.\n\n");
            printf("\tScript_File - the name of the file containing the test\n");
            printf("\t              commands to execute.  The default script file name is\n");
            printf("\t              \"TESTPROT.TPS\".\n\n");
            printf("\tLog_File - the name of the file to log commands and results to.  If\n");
            printf("\t           this file exists, it will be overwritten.  The default\n");
            printf("\t           log file name is \"TESTPROT.LOG\".\n\n");
            printf("\t\"RS\" - the short form of the command.\n");
            break;

        case BEGINLOGGING:
            printf("\tBEGINLOGGING [Log_File]\n\n");
            printf("\tBeginLogging enables the logging of commands and their results.\n");
            printf("\tOnce logging is started any commands entered from the command line\n");
            printf("\tare written to the Log_File.  If commands are being read from\n");
            printf("\ta script this function is disabled. (see also ENDLOGGING)\n\n");
            printf("\tLog_File - the name of the file to log the commands and results to.\n");
            printf("\t           If this file exists, it will be overwritten.  The default\n");
            printf("\t           log file name is \"CMDLINE.LOG\".\n\n");
            printf("\t\"BL\" - the short form of the command.\n");
            break;

        case ENDLOGGING:
            printf("\tENDLOGGING\n\n");
            printf("\tEndLogging disables the logging of commands and their results.\n");
            printf("\t(see also BEGINLOGGING)\n\n");
            printf("\t\"EL\" - the short form of the command.\n");
            break;

        case RECORDINGENABLE:
            printf("\tRECORDINGENABLE [ScriptFile]\n\n");
            printf("\tRecordingEnable enables the recording of commands.\n");
            printf("\tOnce recording is started any commands entered from the command line\n");
            printf("\tare written to the ScriptFile.  If commands are being read from\n");
            printf("\ta script this function is disabled. (see also RECORDINGDISABLE)\n\n");
            printf("\tScriptFile - the name of the file to record the commands and results to.\n");
            printf("\t             If this file exists, it will be overwritten.  The default\n");
            printf("\t             script file name is \"CMDLINE.TPS\".\n\n");
            printf("\t\"RE\" - the short form of the command.\n");
            break;

        case RECORDINGDISABLE:
            printf("\tRECORDINGDISABLE\n\n");
            printf("\tRecordingDisable disables the recording of commands.\n");
            printf("\t(see also RECORDINGENABLE)\n\n");
            printf("\t\"RD\" - the short form of the command.\n");
            break;

        case SHELL:
            printf("\tSHELL [Argument_1 Argument2 ... Argument_N]\n\n");
            printf("\tSHELL will from spawn a command shell from within TPCTL or execute\n");
            printf("\tthe command arguments Argument_1 through Argument_N and return back\n");
            printf("\tto the TPCTL command prompt. If using SHELL by itself, to return to\n");
            printf("\tthe TPCTL prompt simply EXIT the command shell.\n\n");
            printf("\t\"SH\" - the short form of the command.\n");
            break;


        case WAIT:
            printf("\tWAIT [Wait_Time]\n\n");
            printf("\tWait allows a script file to wait a given number of seconds prior\n");
            printf("\tto continuing with the next command.\n\n");
            printf("\tWait_Time - the time in seconds the call will wait before\n");
            printf("\t            returning control to command processing.\n\n");
            printf("\t\"W\" - the short form of the command.\n");
            break;

        case GO:
            printf("\tGO [Open_Instance] [Remote_Address] [Test_Signature]\n\n");
            printf("\tGo sends a TP_GO packet to the Remote Address signalling\n");
            printf("\ta Paused instance of the driver to continue processing its\n");
            printf("\ttest script.  Go continuously resends the packet, and will\n");
            printf("\twait, retrying, until it is acknowledged or stopped with\n");
            printf("\t<Ctrl-C>. (see also PAUSE)\n\n");
            printf("\tOpen_Instance - the open instance between the test driver and\n");
            printf("\t                the MAC adapter that will send the TP_GO Packet.\n");
            printf("\t                The default value is 1.\n\n");
            printf("\tRemote_Address - the address of a remote machine to send the TP_GO\n");
            printf("\t                 packet to.\n\n");
            printf("\tTest_Signature - a unique test signature used by both machines to\n");
            printf("\t                 determine if the correct packets have been sent and\n");
            printf("\t                 acknowledged.  This value must match the Test\n");
            printf("\t                 Signature value on the PAUSED machine.\n\n");
            printf("\t\"G\" - the short form of the command.\n");
            break;

        case PAUSE:
            printf("\tPAUSE [Open_Instance] [Remote_Address] [Test_Signature]\n\n");
            printf("\tPause waits for the receipt of a TP_GO packet wit ha matching test\n");
            printf("\tsignature and then acknowledges it6 by sending a TP_GO_ACKL packet.\n");
            printf("\tPause will wait for the receipt of the TP_GO packlet until it arrives,\n");
            printf("\tor the command is cancelled by <Ctrl-c>.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver and\n");
            printf("\t                the MAC adapter that will wait for the TP_GO Packet.\n");
            printf("\t                The default value is 1.\n\n");
            printf("\tRemote_Address - the address of a remote machine to send the TP_GO_ACK\n");
            printf("\t                 packet to.\n\n");
            printf("\tTest_Signature - a unique test signature used by both machines to\n");
            printf("\t                 determine if the correct packets have been sent and\n");
            printf("\t                 acknowledged.  This value must match the Test\n");
            printf("\t                 Signature value on the machine sending the TP_GO\n");
            printf("\t                 packet\n\n");
            printf("\t\"P\" - the short form of the command.\n");
            break;

        case LOAD:
            printf("\tLOAD [MAC_Driver_Name]\n\n");
            printf("\tLoad issues a call to NtLoadDriver to unload the driver for\n");
            printf("\tthe MAC adapter \"Adapter_Name\".\n\n");
            printf("\tMAC_Driver_Name - the MAC adapter to be loaded. There is no default\n");
            printf("\t                  value.\n\n");
            printf("\t\"L\" - the short form of the command.\n");
            break;

        case UNLOAD:
            printf("\tUNLOAD [MAC_Driver_Name]\n\n");
            printf("\tUnload issues a call to NtUnloadDriver to unload the driver for\n");
            printf("\tthe MAC adapter \"Adapter_Name\".\n\n");
            printf("\tMAC_Driver_Name - the MAC adapter to be unloaded. There is no default\n");
            printf("\t                  value.\n\n");
            printf("\t\"U\" - the short form of the command.\n");
            break;

        case OPEN:
            printf("\tOPEN [Open_Instance] [Adapter_Name]\n\n");
            printf("\tOpen issues a call to NdisOpenAdapter to open the MAC adapter\n");
            printf("\tAdapter_Name, and associates it with the given Open_Instance.\n");
            printf("\tSubsequent calls to the Open_Instance will be directed to\n");
            printf("\tthis adapter.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter this open will be associated\n");
            printf("\t                with.  The default value is 1.\n\n");
            printf("\tAdapter_Name - the MAC adapter to be unloaded. There is no default\n");
            printf("\t               value.\n\n");
            printf("\t\"O\" - the short form of the command.\n");
            break;

        case CLOSE:
            printf("\tCLOSE [Open_Instance]\n\n");
            printf("\tClose issues a call to NdisCloseAdapter to close the MAC adapter\n");
            printf("\tassociated with the given Open_Instance.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to be closed.  The default\n");
            printf("\t                value is 1.\n\n");
            printf("\t\"C\" - the short form of the command.\n");
            break;

        case SETPF:
            printf("\tSETPACKETFILTER [Open_Instance] [Packet_Filter]\n\n");
            printf("\tSetPacketFilter issues a call to the MAC using NdisRequest\n");
            printf("\tto set the card's packet filter to a given value.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to issue the request to.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\tPacket_Filter - the packet filter value to set on the MAC adapter.\n");
            printf("\t                Multiple filter values may be entered by seperating\n");
            printf("\t                each with the \"|\" character.  Valid values for\n");
            printf("\t                Packet_Filter are:\n\n");
            printf("\t                  Directed\n");
            printf("\t                  Multicast\n");
            printf("\t                  AllMulticast\n");
            printf("\t                  Broadcast\n");
            printf("\t                  SourceRouting\n");
            printf("\t                  Promiscuous\n");
            printf("\t                  Mac_Frame\n");
            printf("\t                  Functional\n");
            printf("\t                  AllFunctional\n");
            printf("\t                  Group\n");
            printf("\t                  None\n\n");
            printf("\t                The default value is \"Directed\".\n\n");
            printf("\t\"SP\" - the short form of the command.\n");
            break;

        case SETLA:
            printf("\tSETLOOKAHEADSIZE [Open_Instance] [LookAhead_Size]\n\n");
            printf("\tSetLookAheadSize issues a call to the MAC using NdisRequest\n");
            printf("\tto set the card's lookahead buffer to a given size.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to issue the request to.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\tLookAhead_Size - the new size of the card's lookahead buffer.  The\n");
            printf("\t                 default value is 100 bytes.\n\n");
            printf("\t\"LA\" - the short form of the command.\n");
            break;

        case ADDMA:
            printf("\tADDMULTICASTADDRESS [Open_Instance] [Multicast_Address]\n\n");
            printf("\tAddMulticastAddress issues a call to the MAC using NdisRequest\n");
            printf("\tto add a multicast address to the list of multicast addresses\n");
            printf("\tcurrently set on the card.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to issue the request to.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\tMulticast_Address - the multicast address to add to the list.\n\n");
            printf("\t\"AM\" - the short form of the command.\n");
            break;

        case DELMA:
            printf("\tDELETEMULTICASTADDRESS [Open_Instance] [Multicast_Address]\n\n");
            printf("\tDeleteMulticastAddress issues a call to the MAC using NdisRequest\n");
            printf("\tto delete a multicast address from the list of multicast\n");
            printf("\taddresses currently set on the card.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to issue the request to.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\tMulticast_Address - the multicast address to delete from the list.\n\n");
            printf("\t\"DM\" - the short form of the command.\n");
            break;

        case SETFA:
            printf("\tSETFUNCTIONALADDRESS [Open_Instance] [Functional_Address]\n\n");
            printf("\tSetFunctionalAddress issues a call to the MAC using NdisRequest\n");
            printf("\tto set a functional address on the card.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to issue the request to.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\tFunctional_Address - the functional address to set on the card.\n\n");
            printf("\t\"SF\" - the short form of the command.\n");
            break;

        case SETGA:
            printf("\tSETGROUPADDRESS [Open_Instance] [Group_Address]\n\n");
            printf("\tSetGroupAddress issues a call to the MAC using NdisRequest\n");
            printf("\tto set a group address on the card.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to issue the request to.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\tGroup_Address - the group address to set on the card.\n\n");
            printf("\t\"SG\" - the short form of the command.\n");
            break;

        case QUERYINFO:
            printf("\tQUERYINFORMATION [Open_Instance] [OID_Request]\n\n");
            printf("\tQueryInformation issues a call to the MAC using NdisRequest\n");
            printf("\tto query a given class of information from the MAC.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to issue the request to.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\tOID_Request - the information type to query.  The default value\n");
            printf("\t              is \"SupportedOidList\".\n");
            printf("\t\"QI\" - the short form of the command.\n");
            break;

        case QUERYSTATS:
            printf("\tQUERYSTATISTICS [Device_Name] [OID_Request]\n\n");
            printf("\tDevice_Name - the name of the device to issue the request\n");
            printf("\t              to.  There is no default value.\n\n");
            printf("\tOID_Request - the statistics type to query.  The default value\n");
            printf("\t              is \"SupportedOidList\".\n");
            printf("\t\"QS\" - the short form of the command.\n");
            break;

        case SETINFO:
            printf("\tSETINFORMATION [Open_Instance] [OID_Request] [Type_Specific]\n\n");
            printf("\tSetInformation issues a call to the MAC using NdisRequest\n");
            printf("\tto set a given class of information in the MAC.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to issue the request to.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\tOID_Request - the information type to set.  Valid values for\n");
            printf("\t              OID_Request are:\n\n");
            printf("\t                      CurrentPacketFilter\n");
            printf("\t                      CurrentLookAhead\n");
            printf("\t                      CurrentMulticastList\n");
            printf("\t                      CurrentFunctionalAddress\n");
            printf("\t                      CurrentGroupAddress\n");
            printf("\t              The default value is \"CurrentPacketFilter\".\n");
            printf("\tType_Specific - the information to set for a given OID_Request\n");
            printf("\t\"SI\" - the short form of the command.\n");
            break;

        case RESET:
            printf("\tRESET [Open_Instance]\n\n");
            printf("\tReset issues a call to the MAC using NdisReset to reset the MAC.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to issue the request to.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\t\"R\" - the short form of the command.\n");
            break;

        case SEND:
            printf("\tSEND [Open_Instance] [Destination_Address] [Packet_Size] [Number]\n");
            printf("\t     [Resend_Address]\n\n");
            printf("\tSend issues a call to the MAC using NdisSend to send packets on the\n");
            printf("\tnetwork.  Sending more then one packet causes the command to return\n");
            printf("\tasynchronously.  If a Resend_Address argument is specified, then\n");
            printf("\teach packet sent will contain a \"resend\" packet in the data field\n");
            printf("\tthat is extracted from the packet by any receiving test and\n");
            printf("\tresent to the address specified. (see also RECEIVE, STOPSEND and\n");
            printf("\tWAITSEND)\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to issue the request(s) to.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\tDestination_Address - the network address the packet(s) will be sent\n");
            printf("\t                      to.\n\n");
            printf("\tPacket_Size - the size of the packet(s) to send.\n\n");
            printf("\tNumber - the number of packets to send.  A value of \"-1\" will\n");
            printf("\t         cause the test to send packets continuously until\n");
            printf("\t         stopped by a call to STOPSEND.\n\n");
            printf("\tResend_Address - OPTIONAL: the address that will be placed in the\n");
            printf("\t                 destination address of the \"resend\" packet.\n\n");
            printf("\t\"S\" - the short form of the command.\n");
            break;

        case STOPSEND:
            printf("\tSTOPSEND [Open_Instance]\n\n");
            printf("\tStopSend stops a previously started SEND command if it is still\n");
            printf("\trunning, and prints the SEND command's results.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to stop the SEND command on.\n");
            printf("\t                The default value is 1.\n\n");
            printf("\t\"SS\" - the short form of the command.\n");
            break;

        case WAITSEND:
            printf("\tWAITSEND [Open_Instance]\n\n");
            printf("\tWaitSend waits for a send test to end, and then displays the\n");
            printf("\tsend test results.  This command may be cancelled by entering\n");
            printf("\tCtrl-C.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to wait for the send test to\n");
            printf("\t                end on.  The default value is 1.\n\n");
            printf("\t\"WT\" - the short form of the command.\n");
            break;

        case RECEIVE:
            printf("\tRECEIVE [Open_Instance]\n\n");
            printf("\tReceive sets the test up in a mode to \"expect\" to receive\n");
            printf("\tpackets from other tests.  Each packet will be inspected, and\n");
            printf("\tcounted.  If a test packet received contains a \"resend\"\n");
            printf("\tpacket, the \"resend\" packet will be extracted from the packet,\n");
            printf("\tand sent to the address contained within. (see also SEND and STOPRECEIVE)\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to set up to expect packets.\n");
            printf("\t                The default value is 1.\n\n");
            printf("\t\"RC\" - the short form of the command.\n");
            break;

        case STOPREC:
            printf("\tSTOPRECEIVE [Open_Instance]\n\n");
            printf("\tStopReceive resets a test which has previously had a\n");
            printf("\tRECEIVE commmand issued to it, to no longer \"expect\" packets.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to reset.  The default value\n");
            printf("\t                is 1.\n\n");
            printf("\t\"SR\" - the short form of the command.\n");
            break;

        case GETEVENTS:
            printf("\tGETEVENTS [Open_Instance]\n\n");
            printf("\tGetEvents queries the test for information about \"unexpected\"\n");
            printf("\tindications and completions.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to query the events from.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\t\"GE\" - the short form of the command.\n");
            break;

        case STRESS:
            printf("\tSTRESS [Open_Instance] [Member_Type] [Packets] [Iterations]\n");
            printf("\t       [Packet_Type] [Packet_Size] [Packet_MakeUp] [Response_Type]\n");
            printf("\t       [Delay_Type] [Delay_Length] [Windowing] [Data_Checking]\n");
            printf("\t       [PacketsFromPool]\n\n");
            printf("\tStress sets the test up to run a stress test.  If the test\n");
            printf("\tis started successfully the command will complete asynchronously.\n");
            printf("\tThe test will run until finished or until stopped manually.  (see also\n");
            printf("\tENDSTRESS, STOPSTRESS, WAITSTRESS, and CHECKSTRESS)\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to start a stress test on.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\tMember_Type - how the protocol will perform in the stress test; as\n");
            printf("\t              a client (CLIENT) or as a client and server (BOTH).\n");
            printf("\t              The default value is BOTH.\n\n");
            printf("\tPackets - the number of packets that will be sent to each server prior\n");
            printf("\t          to the test completing.  A value of -1 causes the test to\n");
            printf("\t          run forever unless a value is entered for Iterations.  The\n");
            printf("\t          default value for packets is -1.\n\n");
            printf("\tIterations - the number of iterations this test will run.  A value\n");
            printf("\t             of -1 causes the test to run forever unless a value is\n");
            printf("\t             entered for Packet.  The default value for Iterations\n");
            printf("\t             is -1.\n\n");
            printf("\tPacket_Type - the type of packet size algorithm used to create the\n");
            printf("\t              packets for the test; FIXEDSIZE, RANDOMSIZE or CYCLICAL.\n");
            printf("\t              The default type is FIXED.\n\n");
            printf("\tPacket_Size - with the Packet_Type value determines the size of packets\n");
            printf("\t              in the test.  The default is 512 bytes.\n\n");
            printf("\tPacket_MakeUp - the number and size of the buffers that makeup each\n");
            printf("\t                packet; RAND, SMALL, ZEROS, ONES and KNOWN.  The\n");
            printf("\t                default makeup is RAND.\n\n");
            printf("\tResponse_Type - the method the server will use when responding to test\n");
            printf("\t                packets; NO_RESPONSE, FULL_RESPONSE, ACK_EVERY,\n");
            printf("\t                or ACK_10_TIMES.  The default value is FULL_RESPONSE.\n\n");
            printf("\tDelay_Type - the method used to determine the next interpacket\n");
            printf("\t             delay; FIXEDDELAY or RANDOMDELAY.  The default value\n");
            printf("\t             is FIXEDDELAY.\n\n");
            printf("\tDelay_Length - the minimum number of iterations between two\n");
            printf("\t               consecutive sends to the same server in a test.\n");
            printf("\t               The default value is 0 iterations.\n\n");
            printf("\tWindowing - a boolean used to determine whether a simple windowing\n");
            printf("\t            algorithm will be used between the client and each server.\n");
            printf("\t            the default value is TRUE.\n\n");
            printf("\tData_Checking - a boolean used to determine whether data checking\n");
            printf("\t                will be performed on each packet received.  The\n");
            printf("\t                default value is TRUE.\n\n");
            printf("\tPacketsFromPool - a boolean used to determine whether a pool of\n");
            printf("\t                  packets will be created prior to the test.  The\n");
            printf("\t                  default value is TRUE.\n\n");
            printf("\t\"ST\" - the short form of the command.\n");
            break;

        case STRESSSERVER:
            printf("\tSTRESSSERVER [Open_Instance]\n\n");
            printf("\tStressServer sets the test up to participate in a stress\n");
            printf("\ttest as a server receiving and responding to stress packets from\n");
            printf("\tany clients running a stress test.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to start a stress server on.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\t\"SV\" - the short form of the command.\n");
            break;

        case ENDSTRESS:
            printf("\tENDSTRESS [Open_Instance]\n\n");
            printf("\tEndStress issues a command to the test to stop a currently\n");
            printf("\trunning stress test, whether the protocol is acting as a client or\n");
            printf("\tserver.  If the protocol is acting as a client, once the test has\n");
            printf("\tended, the result will be displayed.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to end the stress test on.  The\n");
            printf("\t                default value is 1.\n\n");
            printf("\t\"ES\" - the short form of the command.\n");
            break;

        case WAITSTRESS:
            printf("\tWAITSTRESS [Open_Instance]\n\n");
            printf("\tWaitStress waits for a stress test to end, and then displays the\n");
            printf("\tstress test results.  This command may be cancelled by entering\n");
            printf("\tCtrl-C.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to wait for the stress test to\n");
            printf("\t                end on.  The default value is 1.\n\n");
            printf("\t\"WS\" - the short form of the command.\n");
            break;

        case CHECKSTRESS:
            printf("\tCHECKSTRESS [Open_Instance]\n\n");
            printf("\tCheckStress checks to see if a stress test has ended, and if so\n");
            printf("\tdisplays the stress test results.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver\n");
            printf("\t                and the MAC adapter to check for the results of a\n");
            printf("\t                stress test on.  The default value is 1.\n\n");
            printf("\t\"CS\" - the short form of the command.\n");
            break;

        case BREAKPOINT:
            printf("\tBREAKPOINT\n\n");
            printf("\tBreakPoint causes an interrupt to break into the debugger.\n\n");
            printf("\t\"BP\" - the short form of the command.\n");
            break;

        case QUIT:
            printf("\tQUIT\n\n");
            printf("\tQuit exits the control application.  Any tests currently\n");
            printf("\trunning are stopped and any opens to MACs are subsequently\n");
            printf("\tclosed.\n\n");
            printf("\t\"Q\" - the short form of the command.\n");
            break;

        case DISABLE:
            printf("\tDISABLE [ENV_VAR_1] [ENV_VAR_2]...[ENV_VAR_N]\n\n");
            printf("\tDisable will prevent the test tool from executing any commands\n");
            printf("\tfollowing it UNLESS all the environment variables passed to it have\n");
            printf("\tbeen declared OR if it encounters the special command ENABLE.\n");
            printf("\tIn that event that all environments variables are set and passed as\n");
            printf("\targuments to DISABLE, the command is ignored and TPCTL remains\n");
            printf("\tactive. Disable by itself will also disable the tool\n\n");
            printf("\t\"DI\" - the short form of the command.\n");
            break;

        case ENABLE:
            printf("\tENABLE\n\n");
            printf("\tEnable will enable the tool to accept commands\n\n");
            printf("\t\"EN\" - the short form of the command.\n");
            break;

        case REGISTRY :
            printf("\tREGISTRY [Operation_Type] [Key_DataBase] [SubKey] [SubKey_Class]\n");
            printf("\t         [SubKey_Value_Name] [SubKey_Value_Type] [SubKey_Value]\n\n");
            printf("\tRegistry is responsible for adding, deleting, modifying and querying\n");
            printf("\texisting registry key entries.\n\n");
            printf("\tOperation_Type - The type of operation to be performed on the registry\n");
            printf("\t                 key\n");
            printf("\t                 Types  : ADD_KEY, DELETE_KEY, QUERY_KEY, ADD_VALUE,\n");
            printf("\t                          CHANGE_VALUE, DELETE_VALUE, QUERY_VALUE\n");
            printf("\t                 Default: QUERY_KEY\n\n");
            printf("\tKey_DataBase   - The key database to be interacted with\n");
            printf("\t                 Databases: CLASSES_ROOT, CURRENT_USER, LOCAL_MACHINE,\n");
            printf("\t                            USER\n");
            printf("\t                 Default  : LOCAL_MACHINE\n\n");
            printf("\tSubKey         - The string value(name) of the subkey being interacted\n");
            printf("\t                 with\n");
            printf("\t                 Default:\n");
            printf("\t                \"System\\CurrenControlSet\\Services\\Elnkii01\\Parameters\"\n");
            printf("\t                 NOTE   : String values must be contained within double\n");
            printf("\t                          quotes\n\n");
            printf("\tSubKey_Class   - The string value(class) to be associated with this\n");
            printf("\t                 subkey\n");
            printf("\t                 Default: \"Network Drivers\"\n");
            printf("\t                 NOTE   : String values must be contained within double\n");
            printf("\t                          quotes\n\n");
            printf("\tSubKey_Value_Name - The string value(ValueName) to be associated with\n");
            printf("\t                    this subkey\n");
            printf("\t                    Default: \"NetworkAddress\"\n");
            printf("\t                    NOTE   : String values must be contained within\n");
            printf("\t                             double quotes\n\n");
            printf("\tSubKey_Value_Type - The type of value being provided\n");
            printf("\t                    Types  : BINARY, DWORD_REGULAR,\n");
            printf("\t                             DWORD_LITTLE_ENDIAN, DWORD_BIG_ENDIAN,\n");
            printf("\t                             EXPAND_SZ, LINK, MULTI_SZ, NONE,\n");
            printf("\t                             RESOURCE_LIST, SZ\n");
            printf("\t                    Default: DWORD_REGULAR\n\n");
            printf("\tSubKey_Value   - The provided value to set the sub key to\n");
            printf("\t                 NOTE : Multiple strings must be seperated by\n");
            printf("\t                        commas. Hex values should be preceeded by 0x.\n");
            printf("\t                        Octal values are preceded by 0. Decimal values\n");
            printf("\t                        do not have a leading 0.By default the base\n");
            printf("\t                        radix is 10\n\n");
            printf("\t\"RG\" - the short form of the command.\n");
            break;

        case PERFSERVER:
            printf("\tPERFORMSERVER [Open_Instance] \n\n");
            printf("\tPerfServer starts a server to participate with the specified client in a\n");
            printf("\tperformance test.  This command always returns synchronously.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver and the MAC\n");
            printf("\t                adapter to issue the request to.  Default value is 1.\n\n");
            printf("\t\"PS\" - the short form of the command.\n");
            break;

        case PERFCLIENT:
            printf("\tPERFORMRECEIVE [Open_Instance] [Server_Address] [Send_Address] ");
            printf(" [Packet_Size] [Num_Packets] [Delay] [Mode] \n\n");
            printf("\tPerfClient starts a client to participate with the specified server in a\n");
            printf("\tperformance test.  The specific test is indicated by the mode.\n");
            printf("\tThis command always returns synchronously.\n\n");
            printf("\tOpen_Instance - the open instance between the test driver and the MAC\n");
            printf("\t                adapter to issue the request to.  Default value is 1.\n\n");
            printf("\tServer_Address - the network address of the server card\n\n");
            printf("\tSend_Address - the network address to which the server sends messages.\n\n");
            printf("\tPacket_Size - total size of the test packets to be sent\n\n");
            printf("\tNum_Packets - total number of test packets to be sent\n\n");
            printf("\tDelay - how much to delay between sends\n\n");
            printf("\tMode - which performance test to use:\n");
            printf("\t       0 = client sends to any address (performance send test)\n");
            printf("\t       1 = client sends to server (performance send test)\n");
            printf("\t       2 = client sends to server, with server ACKs\n");
            printf("\t       3 = two-way sends\n");
            printf("\t       4 = server sends to client (performance receive test)\n");
            printf("\t       5 = client sends REQ to server, server responds with sends\n");
            printf("\t       other = shut down server\n\n");
            printf("\t\"PC\" - the short form of the command.\n");
            break;

        default:
            printf("\tHELP [ ADDMULTICASTADDRESS | BEGINLOGGING | BREAKPOINT | CHECKSTRESS |\n");
            printf("\t       CLOSE | DELETEMULTICASTADDRESS  | ENDLOGGING | ENDSTRESS |\n");
            printf("\t       GETEVENTS | GO | HELP | LOAD | OPEN | PAUSE | QUERYINFORMATION |\n");
            printf("\t       QUERYSTATISTICS | QUIT | READSCRIPT | RECEIVE | RESET | SEND |\n");
            printf("\t       SETENVIRONMENT | SETFUNCTIONALADDRESS | SETGROUPADDRESS |\n");
            printf("\t       SETINFORMATION | SETLOOKAHEADSIZE | SETPACKETFILTER |\n");
            printf("\t       STOPRECEIVE | STOPSEND | STRESS | STRESSSERVER | UNLOAD |\n");
            printf("\t       VERBOSE | WAIT | WAITSEND | WAITSTRESS | SHELL |\n");
            printf("\t       RECORDINGENABLE | RECORDINGDISABLE | REGISTRY |\n");
            printf("\t       PERFSERVER | PERFCLIENT\n\n");
            printf("\tThe command \"%s\" is unknown.\n", _strupr( Command ));
            break;

    } // switch()

    printf("\n");
}



DWORD
TpctlLoadFiles(
    LPSTR ScriptFile,
    LPSTR LogFile
    )

// ---------------
// 
// Routine Description:
// 
//     This routine loads a script file into a buffer, and opens a log
//     file for logging commands and results to.
// 
// Arguments:
// 
//     IN LPSTR ScriptFile - the name of the script file to open and read.
//     IN LPSTR LogFile - the name of the log file to open.
// 
// Return Value:
// 
//     DWORD - NO_ERROR if the script and log files are opened and
//             processed correctly, otherwise the error returned on the
//             failure from the win32 api that failed.
// 
//             NOTE: if this routine returns an error, then TpctlUnLoadFiles
//                   MUST be called next to reset the script structures
//                   correctly, and deallocate any resources that were
//                   allocated during this routine.
//
// --------------- 


{
    DWORD NextScriptIndex;
    HANDLE FileHandle;
    DWORD Status;
    DWORD FileSize;

    NextScriptIndex = ScriptIndex+1;

    //
    // First set the lowest level flag(s) in the scripts field to
    // delineate which script is the lowest VALID script and should be
    // unloaded. (necessary in case the next call to load files fails we
    // will know where the high water mark is.)
    //

    if ( ScriptIndex >= 0 ) 
    {
        //
        // if this is the first script we must ignore the reset of the
        // "previous" script.
        //

        Scripts[ScriptIndex].IsLowestLevel = FALSE;
    }

    Scripts[NextScriptIndex].IsLowestLevel = TRUE;

    //
    // We have a script file, so increment the script index, and set the
    // the index into the script buffer to zero.  Make sure that we have
    // not passed the maximum number of recursion in reading scripts.
    //

    if ( NextScriptIndex == TPCTL_MAX_SCRIPT_LEVELS ) 
    {
        TpctlErrorLog("\n\tTpctl: Too many levels of script reading recursion; level 0x%lx\n",
                                    (PVOID)(NextScriptIndex+1));
        return (DWORD)STATUS_UNSUCCESSFUL;
    }

    //
    // First we allocate the memory to store the script file name in.
    //

    Scripts[NextScriptIndex].ScriptFile = GlobalAlloc(  GMEM_FIXED | GMEM_ZEROINIT,
                                                        TPCTL_MAX_PATHNAME_SIZE );

    if ( Scripts[NextScriptIndex].ScriptFile == NULL ) 
    {
        Status = GetLastError();
        TpctlErrorLog("\n\tTpctlLoadFiles: failed to alloc Script file name storage, returned 0x%lx.\n", (PVOID)Status);
        return Status;
    }

    //
    // Then determine what filename to write to it.
    //

    if ( ScriptFile[0] == '\0' ) 
    {
        //
        // If no script file name was passed, then open the default
        // script file TESTPROT.TPS.
        //

        strcpy( Scripts[NextScriptIndex].ScriptFile,TPCTL_SCRIPTFILE );
    } 
    else 
    {
        //
        // Otherwise copy the filename passed into place.
        //

        strcpy( Scripts[NextScriptIndex].ScriptFile,ScriptFile );
    }

    //
    // Open the script file, if it does not exist fail with an error msg.
    //

    FileHandle = CreateFile(Scripts[NextScriptIndex].ScriptFile,
                            GENERIC_READ,
                            FILE_SHARE_READ,
                            NULL,
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL );

    if ( FileHandle == (HANDLE)-1 ) 
    {
        Status = GetLastError();
        TpctlErrorLog("\n\tTpctl: failed to open script file \"%s\", ",
                        (PVOID)Scripts[NextScriptIndex].ScriptFile);
        TpctlErrorLog("returned 0x%lx.\n",(PVOID)Status);
        return Status;
    }

    //
    // and find its size.
    //

    FileSize = GetFileSize( FileHandle,NULL );

    if ( FileSize == -1 ) 
    {
        Status = GetLastError();
        TpctlErrorLog("\n\tTpctl: failed find file size - returned 0x%lx.\n",
                        (PVOID)Status);
        return Status;
    }

    //
    // If necessary allocate memory for the Buffer.
    //

    if ( Scripts[NextScriptIndex].Buffer == NULL ) 
    {
        Scripts[NextScriptIndex].Buffer = (LPBYTE)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT,
                                                               FileSize );

        if ( Scripts[NextScriptIndex].Buffer == NULL ) 
        {
            Status = GetLastError();
            TpctlErrorLog("\n\tTpctlLoadFiles: failed to alloc script buffer, returned 0x%lx.\n",
                                (PVOID)Status);
            CloseHandle( FileHandle );
            return Status;
        }

    } 
    else if ( FileSize > Scripts[NextScriptIndex].Length ) 
    {
        Scripts[NextScriptIndex].Buffer = 
                    (LPBYTE)GlobalReAlloc( (HANDLE)Scripts[NextScriptIndex].Buffer,
                                            FileSize,
                                            GMEM_ZEROINIT | GMEM_MOVEABLE );

        if ( Scripts[NextScriptIndex].Buffer == NULL ) 
        {
            Status = GetLastError();
            TpctlErrorLog("\n\tTpctlLoadFiles: failed to ReAlloc script buffer, returned 0x%lx.\n",
                            (PVOID)Status);
            CloseHandle( FileHandle );
            return Status;
        }
    }

    //
    // And read the script file into it.
    //

    Status = ReadFile(  FileHandle,
                        Scripts[NextScriptIndex].Buffer,
                        FileSize,
                        &Scripts[NextScriptIndex].Length,
                        NULL );

    if ( Status != TRUE ) 
    {
        Status = GetLastError();
        TpctlErrorLog("\n\tTpctlLoadFiles: failed to read script file \"%s\", ",(PVOID)ScriptFile);
        TpctlErrorLog("returned 0x%lx.\n",(PVOID)Status);
        CloseHandle( FileHandle );
        return Status;
    }

    //
    // We are done with script file now, so close it.
    //

    if (!CloseHandle(FileHandle)) 
    {
        Status = GetLastError();
        TpctlErrorLog("\n\tTpctlLoadFiles: failed to close Script file \"%s\", ",(PVOID)ScriptFile);
        TpctlErrorLog("returned 0x%lx.\n",(PVOID)Status);
    }

    //
    // Now handle the log file.  If we are not given a log file we need
    // to determine the name of the log file we should use.
    // First we allocate the memory to store the log file name in.
    //

    Scripts[NextScriptIndex].LogFile = GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT,
                                                    TPCTL_MAX_PATHNAME_SIZE );

    if ( Scripts[NextScriptIndex].LogFile == NULL ) 
    {
        Status = GetLastError();
        TpctlErrorLog(
                    "\n\tTpctlLoadFiles: failed to alloc Log file name storage, returned 0x%lx.\n",
                        (PVOID)Status);
        return Status;
    }

    //
    // Then determine what filename to write to it.
    //

    if (( LogFile == NULL ) || ( LogFile[0] == '\0' )) 
    {
        if ( NextScriptIndex == 0 ) 
        {
            //
            // If this is the first script file and no log file name was
            // given, then use the default log file name.
            //

            strcpy( Scripts[NextScriptIndex].LogFile,TPCTL_LOGFILE );
        } 
        else 
        {
            //
            // Otherwise, since no new log file name was given, and we are
            // recursively reading script files we will use the log file
            // used by the last level of script files.
            //

            strcpy( Scripts[NextScriptIndex].LogFile,Scripts[ScriptIndex].LogFile );
            Scripts[NextScriptIndex].LogHandle = Scripts[ScriptIndex].LogHandle;
        }

    } 
    else 
    {
        //
        // We have a log file name so copy it into the scripts structure.
        //

        strcpy(Scripts[NextScriptIndex].LogFile,LogFile);
    }

    //
    // Now, if the log file has not already been opened, then we must open
    // it.  If the logfile already exists it WILL be truncated on the open.
    //

    if (( LogFile != NULL ) && ( LogFile[0] != '\0' ) ||
        ( NextScriptIndex == 0 )) 
    {
        if ( WriteThrough ) 
        {
            Scripts[NextScriptIndex].LogHandle =
                                CreateFile( Scripts[NextScriptIndex].LogFile,
                                            GENERIC_WRITE,
                                            FILE_SHARE_WRITE | FILE_SHARE_READ,
                                            NULL,
                                            CREATE_ALWAYS,
                                            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
                                            NULL );
        } 
        else 
        {
            Scripts[NextScriptIndex].LogHandle =
                                CreateFile( Scripts[NextScriptIndex].LogFile,
                                            GENERIC_WRITE,
                                            FILE_SHARE_WRITE | FILE_SHARE_READ,
                                            NULL,
                                            CREATE_ALWAYS,
                                            FILE_ATTRIBUTE_NORMAL,
                                            NULL );
        }

        if ( Scripts[NextScriptIndex].LogHandle == (HANDLE)-1 ) 
        {
            Status = GetLastError();
            TpctlErrorLog("\n\tTpctl: failed to open log file \"%s\", ",
                                        (PVOID)Scripts[NextScriptIndex].LogFile);
            TpctlErrorLog("returned 0x%lx.\n",(PVOID)Status);
            return Status;
        }
    }

    //
    // We have successfully opened the script and log files, and are now
    // ready to read commands from the script buffer, set the flag stating
    // that the commands are coming from the script file, and increment the
    // scriptindex to point to the newly create script info.
    //

    CommandsFromScript = TRUE;

    ScriptIndex = NextScriptIndex;

    return NO_ERROR;
}



VOID
TpctlFreeFileBuffers(
    VOID
    )

// ---------------
// 
// Routine Description:
// 
// Arguments:
// 
//     None.
// 
// Return Value:
// 
//     None.
// 
// --------------

{
    DWORD si = 0;
    HANDLE tmpHandle;
    DWORD Status;

    for (si=0;si<TPCTL_MAX_SCRIPT_LEVELS;si++) 
    {
        if ( Scripts[si].Buffer != NULL ) 
        {
            tmpHandle = GlobalFree( (HANDLE)Scripts[si].Buffer );

            if ( tmpHandle != NULL ) 
            {
                Status = GetLastError();
                TpctlErrorLog("\n\tTpctlFreeFileBuffers: GlobalFree failed: returned 0x%lx.\n",
                    (PVOID)Status);
            }
        }

        Scripts[si].Buffer = NULL;
        Scripts[si].Length = 0;
    }
}



VOID
TpctlUnLoadFiles(
    VOID
    )

// ---------------
// 
// Routine Description:
// 
// Arguments:
// 
//     None.
// 
// Return Value:
// 
//     None.
// 
// --------------

{
    DWORD si;
    HANDLE tmpHandle;
    DWORD Status;

    //
    // TpctlUnloadFiles may be called to unload a file that is no longer
    // needed, or a file that was not successfully loaded by TpctlLoadFiles.
    // If the file to be unloaded is one that failed to load during the load
    // files routine, then the ScriptIndex does not point to the correct
    // field in the script array, so we must adjust the index pointer
    // to the next field, otherwise just unload the file pointed by the
    // ScriptIndex.
    //

    si = ScriptIndex;

    if (( ScriptIndex < 0 ) || ( Scripts[si].IsLowestLevel == FALSE )) 
    {
        si++;
    }

    //
    // Free up the memory used to store the file names, and the
    // script file commands.
    //

    if ( Scripts[si].ScriptFile != NULL ) 
    {
        tmpHandle = GlobalFree( Scripts[si].ScriptFile );

        if ( tmpHandle != NULL ) 
        {
            Status = GetLastError();
            TpctlErrorLog("\n\tTpctlUnLoadFiles: GlobalFree failed: returned 0x%lx.\n",
                                (PVOID)Status);
        }
    }

    if ( Scripts[si].LogFile != NULL ) 
    {
        tmpHandle = GlobalFree( Scripts[si].LogFile );

        if ( tmpHandle != NULL ) 
        {
            Status = GetLastError();
            TpctlErrorLog("\n\tTpctlUnLoadFiles: GlobalFree failed: returned 0x%lx.\n",
                                (PVOID)Status);
        }
    }

    //
    // Do we have a unique log file, or was the log file opened by a higher
    // order recursion of the TpctlLoadFiles routine?
    //

    if (( Scripts[si].LogHandle != (HANDLE)-1 ) &&    // log handle exists
       (( si == 0 ) ||                                // first level of recursion
        ( Scripts[si].LogHandle != Scripts[si-1].LogHandle ))) 
    {

        //
        // This level of the ReadScript command opened the log file, so
        // we must close it now.
        //

        CloseHandle( Scripts[si].LogHandle );
    }

    //
    // Now set all the fields to their intial state.
    //

    Scripts[si].ScriptFile = NULL;
    Scripts[si].BufIndex = 0;
    Scripts[si].LogHandle = (HANDLE)-1;
    Scripts[si].LogFile = NULL;
    Scripts[si].IsLowestLevel = FALSE;

    //
    // If we are simply unloading a script that we are finished with
    // then decrement the index into the Scripts array to reference the
    // next higher level, if it exists, in the ReadScript recursion.
    // If we are unloading the highest level script file then reset
    // the commandsfromscript flag to state that we no longer are
    // reading the commands from a script file.
    //

    if ( si == ScriptIndex ) 
    {
        if ( --ScriptIndex == -1 ) 
        {
            CommandsFromScript = FALSE;
            TpctlFreeFileBuffers();
        }
    }

    if ( si != 0 ) 
    {
        Scripts[si-1].IsLowestLevel = TRUE;
    }
}



HANDLE
TpctlOpenLogFile(
    VOID
    )

// -------------
// 
// Routine Description:
// 
// Arguments:
// 
// Return Value:
// 
// ------------

{
    HANDLE LogHandle;
    DWORD Status;

    if ( WriteThrough ) 
    {
        LogHandle = CreateFile( GlobalCmdArgs.ARGS.FILES.LogFile,
                                GENERIC_WRITE,
                                FILE_SHARE_WRITE | FILE_SHARE_READ,
                                NULL,
                                CREATE_ALWAYS,
                                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
                                NULL );
    } 
    else 
    {
        LogHandle = CreateFile( GlobalCmdArgs.ARGS.FILES.LogFile,
                                GENERIC_WRITE,
                                FILE_SHARE_WRITE | FILE_SHARE_READ,
                                NULL,
                                CREATE_ALWAYS,
                                FILE_ATTRIBUTE_NORMAL,
                                NULL );
    }


    if ( LogHandle == (HANDLE)-1 ) 
    {
        Status = GetLastError();
        TpctlErrorLog("\n\tTpctl: failed to open log file\"%s\", ",
            (PVOID)GlobalCmdArgs.ARGS.FILES.LogFile);
        TpctlErrorLog("returned 0x%lx.\n",(PVOID)Status);
    }

    return LogHandle;
}



VOID
TpctlCloseLogFile(
    VOID
    )

{
    DWORD Status;

    if (!CloseHandle( CommandLineLogHandle )) 
    {
        Status = GetLastError();
        TpctlErrorLog("\n\tTpctlCloseLogFile: failed to close Log file; returned 0x%lx.\n",
                            (PVOID)Status);
    }

    return;
}



HANDLE
TpctlOpenScriptFile(
    VOID
    )

// ----------------
// 
// Routine Description:
// 
//    Created    Sanjeevk  7-1-93
// 
//    This is a new function defined for the purpose of opening up a file
//    to which commands will be written
// 
// Arguments:
// 
//     None
// 
// Global Arguments effected:
// 
//     RecordScriptName
// 
// Return Value:
// 
//     A HANDLE to the script file or NULL
// 
// ---------------

{
    HANDLE ScriptHandle;
    DWORD Status;


    //
    // 1. Clear the global variable and copy in the name of the file
    //    to be opened
    //

    memset( RecordScriptName, 0, (TPCTL_MAX_PATHNAME_SIZE*sizeof(CHAR)) );
    strcpy( RecordScriptName, GlobalCmdArgs.ARGS.RECORD.ScriptFile );

    if ( WriteThrough ) 
    {
        ScriptHandle = CreateFile(  GlobalCmdArgs.ARGS.RECORD.ScriptFile,
                                    GENERIC_WRITE,
                                    FILE_SHARE_WRITE | FILE_SHARE_READ,
                                    NULL,
                                    CREATE_ALWAYS,
                                    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
                                    NULL );
    } 
    else 
    {
        ScriptHandle = CreateFile(  GlobalCmdArgs.ARGS.RECORD.ScriptFile,
                                    GENERIC_WRITE,
                                    FILE_SHARE_WRITE | FILE_SHARE_READ,
                                    NULL,
                                    CREATE_ALWAYS,
                                    FILE_ATTRIBUTE_NORMAL,
                                    NULL );
    }


    if ( ScriptHandle == (HANDLE)-1 ) 
    {
        Status = GetLastError();
        ZeroMemory( RecordScriptName, (TPCTL_MAX_PATHNAME_SIZE*sizeof(CHAR)) );
        TpctlErrorLog("\n\tTpctl: failed to open script recording file\"%s\", ",
            (PVOID)GlobalCmdArgs.ARGS.RECORD.ScriptFile);
        TpctlErrorLog("returned 0x%lx.\n",(PVOID)Status);
    }

    return ScriptHandle;
}



VOID
TpctlCloseScriptFile(
    VOID
    )

// ---------------
// 
// Routine Description:
// 
//    Created    Sanjeevk  7-1-93
// 
//    This is a new function defined for the purpose of closing a file
//    to which commands were being written
// 
// Arguments:
// 
//     None
// 
// Global Arguments effected:
// 
//     RecordScriptName
//     ScriptRecordHandle
// 
// Return Value:
// 
//     None
// 
// --------------- 

{
    DWORD Status;

    ZeroMemory( RecordScriptName, (TPCTL_MAX_PATHNAME_SIZE*sizeof(CHAR)) );

    if (!CloseHandle( ScriptRecordHandle )) 
    {
        Status = GetLastError();
        TpctlErrorLog(
                "\n\tTpctlCloseScriptFile: failed to close script record file; returned 0x%lx.\n",
                        (PVOID)Status);
    }

    return;
}




DWORD
TpctlReadCommand(
    IN LPSTR Prompt,
    OUT LPSTR Buffer,
    IN DWORD MaximumResponse
    )

// --------------
// 
// Routine Description:
// 
//     This routine reads from the debug port or the command file one command.
// 
// Arguments:
// 
//     IN LPSTR Prompt,
//     OUT LPSTR Buffer,
//     IN DWORD MaximumResponse,
// 
// Return Value:
// 
//     DWORD - NO_ERROR
// 
// -------------

{
    DWORD Status = NO_ERROR;
    LPSTR CmdBufPtr = Buffer;
    DWORD i, j, k;
    BYTE LineBuf[TPCTL_CMDLINE_SIZE];
    BYTE TmpBuf[TPCTL_CMDLINE_SIZE];
    LPBYTE EndOfCmd;
    LPBYTE SBuf;
    BOOL ContinueCommand = FALSE;
    BOOL InitialCommand = TRUE;
    BOOL FoundEnvVar;
    LPSTR EnvVar;

    //
    // If the ScriptIndex equals -1 we are reading commands from the
    // command line, so we will prompt the user to enter commands.
    //

    if ( ScriptIndex == -1 ) 
    {
        TpctlPrompt( Prompt,LineBuf,MaximumResponse );

        i = 0; // LineBuf index
        k = 0; // Buffer index

        while (( i < TPCTL_CMDLINE_SIZE ) &&
              (( LineBuf[i] != '\n' ) &&
               ( LineBuf[i] != '\r' ))) 
        {
            //
            // If we have found the beginning of an Environment
            // Variable argument
            //

            if ( LineBuf[i] == '%' ) 
            {
                j = (DWORD)-1;
                FoundEnvVar = FALSE;
                i++;

                //
                // Copy it into a temp buffer.
                //

                while (( LineBuf[i] != '\n' ) &&
                      (( LineBuf[i] != ' ' ) &&
                      (( LineBuf[i] != '\t' ) &&
                       ( LineBuf[i] != '\r' )))) 
                {
                    TmpBuf[++j] = LineBuf[i++];

                    if ( TmpBuf[j] == '%') 
                    {
                        TmpBuf[j] = '\0';
                        FoundEnvVar = TRUE;
                        break;
                    }
                }

                TmpBuf[j] = '\0';

                //
                // And find its true value in the process environment.
                //

                if ( FoundEnvVar == TRUE ) 
                {
                    EnvVar = getenv( _strupr( TmpBuf ));

                    if ( EnvVar == NULL ) 
                    {
                        TpctlErrorLog("\n\tTpctl: Undefined Environment Variable \"%%%s%%\".\n",
                                                    TmpBuf);
                        return ERROR_ENVVAR_NOT_FOUND;
                    }

                    //
                    // and copy the value to the line buffer.
                    //

                    do 
                    {
                        Buffer[k++] = *EnvVar++;
                    } while ( *EnvVar != '\0' );

                } 
                else 
                {
                    TmpBuf[++j] = '\0';
                    TpctlErrorLog("\n\tTpctl: Invalid Environment Variable Format \"%%%s\".\n",
                                                TmpBuf);
                    return ERROR_INVALID_PARAMETER;
                }

            //
            // Otherwise just copy the next character to the line buffer.
            //

            } 
            else 
            {
                Buffer[k++] = LineBuf[i++];
            }
        }

        //
        // and then print the commands to the log file if necessary.
        //

        TpctlCmdLneLog(" %s\n", Buffer);

    //
    // Otherwise we are reading commands from a script file, so return
    // the next command in the file.
    //

    } 
    else if ( Scripts[ScriptIndex].BufIndex >= Scripts[ScriptIndex].Length ) 
    {
        //
        // We are at the end of this script file, clean up the script
        // and log files.
        //

        TpctlUnLoadFiles();

        //
        // Set the return value in Buffer to null indicating that
        // there was no command.
        //

        *Buffer = 0x0;

    } 
    else 
    {
        //
        // Null out the Buffer buffer so that we don't use any garbage
        // laying around from the last command.
        //

        ZeroMemory(Buffer, TPCTL_CMDLINE_SIZE);

        SBuf = Scripts[ScriptIndex].Buffer;

        while ((DWORD)(CmdBufPtr - Buffer) < MaximumResponse ) 
        {
            //
            // and null out the temporary command buffer.
            //

            ZeroMemory(LineBuf, TPCTL_CMDLINE_SIZE);

            //
            // Read the next command line from the script file.
            //

            i = (DWORD)-1;

            while ( Scripts[ScriptIndex].BufIndex <
                    Scripts[ScriptIndex].Length ) 
            {
                //
                // If we have found the beginning of an Environment
                // Variable argument...
                //

                if ( SBuf[Scripts[ScriptIndex].BufIndex] == '%' ) 
                {
                    j = (DWORD)-1;
                    FoundEnvVar = FALSE;
                    Scripts[ScriptIndex].BufIndex++;

                    //
                    // Copy it into a temp buffer.
                    //

                    while (( SBuf[Scripts[ScriptIndex].BufIndex] != '\n' ) &&
                          (( SBuf[Scripts[ScriptIndex].BufIndex] != ' ' ) &&
                          (( SBuf[Scripts[ScriptIndex].BufIndex] != '\t' ) &&
                           ( SBuf[Scripts[ScriptIndex].BufIndex] != '\r' )))) 
                    {
                        TmpBuf[++j] = SBuf[Scripts[ScriptIndex].BufIndex++];

                        if ( TmpBuf[j] == '%') 
                        {
                            TmpBuf[j] = '\0';
                            FoundEnvVar = TRUE;
                            break;
                        }
                    }

                    //
                    // And find its true value in the process environment.
                    //

                    if ( FoundEnvVar == TRUE ) 
                    {
                        EnvVar = getenv( _strupr( TmpBuf ));

                        if ( EnvVar == NULL ) 
                        {
                            TpctlErrorLog("\n\tTpctl: Undefined Environment Variable \"%%%s%%\".\n",
                                                TmpBuf);
                            return ERROR_ENVVAR_NOT_FOUND;
                        }

                        //
                        // and copy the value to the line buffer.
                        //

                        do 
                        {
                            LineBuf[++i] = *EnvVar++;
                        } while ( *EnvVar != '\0' );

                    } 
                    else 
                    {
                        TmpBuf[++j] = '\0';
                        TpctlErrorLog("\n\tTpctl: Invalid Environment Variable Format \"%%%s\".\n",
                                                    TmpBuf);
                        return ERROR_INVALID_PARAMETER;
                    }

                //
                // Otherwise just copy the next character to the line buffer.
                //

                } 
                else 
                {
                    LineBuf[++i] = SBuf[Scripts[ScriptIndex].BufIndex++];
                }

                if ( LineBuf[i] == '\n' ) 
                {
                    break;
                }
            }

            LineBuf[i] = '\0';

            if ( InitialCommand == TRUE ) 
            {
                TpctlLog("%s ",Prompt);
                InitialCommand = FALSE;
            } 
            else 
            {
                TpctlLog("\t ",NULL );
            }

            TpctlLog("%s\n",LineBuf);

            if ( !Verbose ) 
            {
                if ( strstr( LineBuf,"TITLE:" ) != NULL ) 
                {
                    TpctlErrorLog("\n%s ",Prompt);
                    TpctlErrorLog("%s\n\n",LineBuf);
                }
            }

            // check for comment ending line

            ContinueCommand = FALSE;
            if ( (EndOfCmd = strchr( LineBuf, '#')) != NULL)   
            {
                //
                // We just have a comment, set the command continue
                // flag to exit the command parsing, and null the
                // command section of the string.
                //
                EndOfCmd[0] = '\0';
            }

            // check for a closing parenthesis on line.  This is the end of any SETGLOBALS
            // command that contains an expression.  No other command uses parenthesis

            if ( (EndOfCmd = strchr( LineBuf, ')' )) != NULL)
            {
                EndOfCmd[1] = '\0';     // closing parenthese is last thing on line
            }
            else if ( (EndOfCmd = strchr( LineBuf, '+' )) != NULL)
            {
                //
                // This is a Cmd Continuation, set the flag to continue
                // the while loop, and ignore the rest of the line.
                //
                ContinueCommand = TRUE;
                EndOfCmd[0] = '\0';
            } 

            i=0;

            while ( LineBuf[i] != '\0' ) 
            {
                if ((( LineBuf[i] == ' ' ) ||
                     ( LineBuf[i] == '\t' )) ||
                     ( LineBuf[i] == '\r' )) 
                {
                    *CmdBufPtr++ = ' ';

                    while ((( LineBuf[i] == ' ' ) ||
                            ( LineBuf[i] == '\t' )) ||
                            ( LineBuf[i] == '\r' )) 
                    {
                        i++;
                    }

                } 
                else 
                {
                    *CmdBufPtr++ = LineBuf[i++];
                }
            }

            if ( ContinueCommand == FALSE ) 
            {
                return Status;
            }
        }
    }

    return Status;
}



BOOL
TpctlParseCommand(
    IN LPSTR CommandLine,
    OUT LPSTR Argv[],
    OUT PDWORD Argc,
    IN DWORD MaxArgc
    )
{
    LPSTR cl = CommandLine;
    DWORD ac = 0;
    BOOL DoubleQuotesDetected, DetectedEndOfString, StartOfString;

    while ( *cl && (ac < MaxArgc) ) 
    {
        //
        // Skip to get to the lvalue
        //
        while ( *cl && (*cl <= ' ') )   // ignore leading blanks
        {
            cl++;
        }

        if ( !*cl ) 
        {
            break;
        }

        //
        // Argument detected. Initialize the Argv and increment the counter
        //

        *Argv++ = cl;
        ++ac;

        DoubleQuotesDetected = DetectedEndOfString = FALSE;
        StartOfString = TRUE;

        while( !DetectedEndOfString ) 
        {
            while ( *cl > ' ') 
            {
                if ( StartOfString && (*cl == '"') && (*(cl-1) == '=')  ) 
                {
                    DoubleQuotesDetected = TRUE;
                    StartOfString = FALSE;
                }
                cl++;
            }

            if ( DoubleQuotesDetected ) 
            {
                if ( ((*(cl-1) == '"') && (*(cl-2) != '\\')) ||
                     ( *cl != ' ' ) ) 
                {
                    DetectedEndOfString = TRUE;
                } 
                else 
                {
                    cl++;
                }
            } 
            else 
            {
                DetectedEndOfString = TRUE;
            }
        }

        if ( *cl ) 
        {
            *cl++ = '\0';
        }

    }

    if ( ac < MaxArgc ) 
    {
        *Argv++ = NULL;
    } 
    else if ( *cl ) 
    {
        TpctlErrorLog("\n\tTpctl: Too many tokens in command; \"%s\".\n",(PVOID)cl);
        return FALSE;
    }

    *Argc = ac;

    return TRUE;
}



VOID
TpctlPrompt(
    LPSTR Prompt,
    LPSTR Buffer,
    DWORD BufferSize
    )

// -----------
// 
// Routine Description:
// 
// 
// Arguments:
// 
//     Prompt -
//     Buffer -
//     BufferSize -
// 
// Return Value:
// 
//     None.
// 
// ----------

{
    LPSTR NewLine;
    DWORD ReadAmount;

    //
    // print out the prompt command, and then read the user's input.
    // We are using the TpctlErrorLog routine to print it to the
    // screen and the log files because we know that verbose mode
    //

    TpctlErrorLog("%s ",Prompt);

    ReadFile(   GetStdHandle(STD_INPUT_HANDLE),
                (LPVOID )Buffer,
                BufferSize,
                &ReadAmount,
                NULL );

    //
    //  If the user typed <CR>, then the buffer contains a single
    //  <CR> character.  We want to remove this character, and replace it with
    //  a nul character.
    //

    if ( (NewLine = strchr(Buffer, '\r')) != NULL ) 
    {
        *NewLine = '\0';
    }

}



VOID
TpctlLoadLastEnvironmentVariables(
    DWORD OpenInstance
    )

// --------------
// 
// Routine Description:
// 
// Arguments:
// 
// Return Value:
// 
//     None.
// 
// -------------

{
    GlobalCmdArgs.ARGS.ENV.WindowSize =
        Open[OpenInstance].EnvVars->WindowSize;

    GlobalCmdArgs.ARGS.ENV.RandomBufferNumber =
        Open[OpenInstance].EnvVars->RandomBufferNumber;

    GlobalCmdArgs.ARGS.ENV.StressDelayInterval =
        Open[OpenInstance].EnvVars->StressDelayInterval;

    GlobalCmdArgs.ARGS.ENV.UpForAirDelay =
        Open[OpenInstance].EnvVars->UpForAirDelay;

    GlobalCmdArgs.ARGS.ENV.StandardDelay =
        Open[OpenInstance].EnvVars->StandardDelay;

    strcpy( GlobalCmdArgs.ARGS.ENV.StressAddress,
            Open[OpenInstance].EnvVars->StressAddress );

    strcpy( GlobalCmdArgs.ARGS.ENV.ResendAddress,
            Open[OpenInstance].EnvVars->ResendAddress );
}



VOID
TpctlSaveNewEnvironmentVariables(
    DWORD OpenInstance
    )

// ---------------
// 
// Routine Description:
// 
// Arguments:
// 
//     None.
// 
// Return Value:
// 
//     None.
// 
// -------------

{
    Open[OpenInstance].EnvVars->WindowSize =
        GlobalCmdArgs.ARGS.ENV.WindowSize;

    Open[OpenInstance].EnvVars->WindowSize =
        GlobalCmdArgs.ARGS.ENV.RandomBufferNumber;

    Open[OpenInstance].EnvVars->StressDelayInterval =
        GlobalCmdArgs.ARGS.ENV.StressDelayInterval;

    Open[OpenInstance].EnvVars->UpForAirDelay =
        GlobalCmdArgs.ARGS.ENV.UpForAirDelay;

    Open[OpenInstance].EnvVars->StandardDelay =
        GlobalCmdArgs.ARGS.ENV.StandardDelay;

    strcpy( Open[OpenInstance].EnvVars->StressAddress,
            GlobalCmdArgs.ARGS.ENV.StressAddress );

    strcpy( Open[OpenInstance].EnvVars->ResendAddress,
            GlobalCmdArgs.ARGS.ENV.ResendAddress );
}


// !!check calls here for WIN32!!

VOID
TpctlPerformRegistryOperation(
                    IN PCMD_ARGS CmdArgs
                             )
{
    DWORD          Status,ValueType,ValueSize  ;
    DWORD          ReadValueType, ReadValueSize;
    DWORD          Disposition  , BytesWritten ;
    PUCHAR         ReadValue = NULL      ;
    UCHAR          PrintStringBuffer[10], TmpChar;
    HKEY           DbaseHKey, KeyHandle  ;
    REGSAM         SamDesired         ;
    LPSTR          TmpBuf = GlobalBuf, StopString ;
    LPSTR          SubKeyName = &CmdArgs->ARGS.REGISTRY_ENTRY.SubKey[1]        ;
    LPSTR          ValueName = &CmdArgs->ARGS.REGISTRY_ENTRY.SubKeyValueName[1];
    LPSTR          Value = CmdArgs->ARGS.REGISTRY_ENTRY.SubKeyValue            ;
    LPSTR          DbaseName = KeyDbaseTable[CmdArgs->ARGS.REGISTRY_ENTRY.OperationType].FieldName;
    LPSTR          ClassName = &CmdArgs->ARGS.REGISTRY_ENTRY.SubKeyClass[1]    ;
    LPSTR          Tmp = NULL;
    BOOL           CompleteQueryStatus;
    INT            i,j,k,Radix = 16,CopyLength = 2;


    TmpBuf += (BYTE)sprintf(TmpBuf,"\n\tCommandCode    = %s\n", 
                            TpctlGetCmdCode( CmdArgs->CmdCode ));

    //
    // Initialize and allocate resources
    //

    if ( (ReadValue = calloc( 2, MAX_VALUE_LENGTH )) == NULL ) 
    {
        TpctlErrorLog( "\n\tTpctl: TpctlPeformRegistryOperation: Unable to allocate memory resources\n", NULL );
        return;
    }

    //
    // Clear and write the buffer responsible for extracting the values
    //

    ZeroMemory ( PrintStringBuffer, sizeof( PrintStringBuffer ) );
    sprintf( PrintStringBuffer, "%%%d.%dx", sizeof(DWORD), sizeof(DWORD) );

    //
    // Set the appropriate DataBase key
    //

    switch(  CmdArgs->ARGS.REGISTRY_ENTRY.KeyDatabase ) 
    {
        case CLASSES_ROOT : 
            DbaseHKey = HKEY_CLASSES_ROOT;
            break;

        case CURRENT_USER : 
            DbaseHKey = HKEY_CURRENT_USER;
            break;

        case LOCAL_MACHINE: 
            DbaseHKey = HKEY_LOCAL_MACHINE;
            break;

        case USERS: 
            DbaseHKey = HKEY_USERS;
            break;

        default: 
            TpctlErrorLog("\n\tTpctl: %d not a valid Key DataBase",
                            (PVOID)CmdArgs->ARGS.REGISTRY_ENTRY.KeyDatabase );
            return;
    }

    //
    // The SubKey Name
    // The Value name
    // The Class Name
    //
    if ( (Tmp = strrchr( SubKeyName, '"' ))  != NULL ) 
    {
        *Tmp = '\0';
    }
    if ( (Tmp = strrchr( ValueName, '"' ))  != NULL ) 
    {
        *Tmp = '\0';
    }
    if ( (Tmp = strrchr( ClassName, '"' ))  != NULL ) 
    {
        *Tmp = '\0';
    }

    //
    // The value type and the associated value
    //
    
    switch( CmdArgs->ARGS.REGISTRY_ENTRY.ValueType ) 
    {
        case BINARY :
            ValueType = REG_BINARY;
            i         = 0;
            j         = 0;  // Default begin extraction from String[j=0]:Value buffer starting point
            k         = 0;  // Default:Input Value is in hex or binary - designator. 
                            // 0 is HEX, 1 is BINARY
            ValueSize = strlen( Value );
            if( ValueSize >= 2 ) 
            {
                if ( toupper( Value[1] ) == 'B' ) 
                {
                    j          = 2;
                    k          = 1;
                    Radix      = 2;
                    CopyLength = 8;
                }
            }
            {
                UCHAR  BitStream[9];
                PUCHAR PTmpChar;
                DWORD  BytesToCopy;

                while( j < (INT)ValueSize ) 
                {
                    memset( BitStream, '\0', sizeof( BitStream ) );
                    memset( BitStream, '0' , sizeof(UCHAR)*CopyLength );
                    BytesToCopy = min( strlen( &Value[j] ), (DWORD)CopyLength );
                    memcpy( BitStream, &Value[j], BytesToCopy );
                    Value[i] = (UCHAR)strtoul( BitStream,&PTmpChar, Radix );
                    i++;
                    j += BytesToCopy;
                }
                ValueSize = i;
            }
            break;

        case DWORD_REGULAR :
            ValueType = REG_DWORD;
            ValueSize = sizeof( DWORD );
            *(LPDWORD)Value = strtoul( Value, &StopString, 0 );
            break;

        case DWORD_LITTLE_ENDIAN :
            ValueType = REG_DWORD_LITTLE_ENDIAN;
            ValueSize = sizeof( DWORD );
            {
                DWORD TmpValue =  strtoul( Value, &StopString, 0 );
                sprintf( Value, PrintStringBuffer, TmpValue );
            }
            // Reverse the array since this is Big Endian

            for( i = 0, j = ValueSize-1; i < (INT)ValueSize; i++,j-- ) 
            {
                Value[i] -= '0';
                Value[j] -= '0';
                TmpChar = Value[i];
                Value[i] = Value[j];
                Value[j] = TmpChar;
            }
            break;

        case DWORD_BIG_ENDIAN :
            ValueType = REG_DWORD_BIG_ENDIAN;
            ValueSize = sizeof( DWORD );
            {
                DWORD TmpValue =  strtoul( Value, &StopString, 0 );
                sprintf( Value, PrintStringBuffer, TmpValue );
            }
            break;

        case EXPAND_SZ :
            ValueType = REG_EXPAND_SZ;
            ValueSize = strlen( Value );
            break;

        case LINK :
            ValueType = REG_LINK;
            ValueSize = strlen( Value );
            break;

        case MULTI_SZ :
            ValueType = REG_MULTI_SZ;
          
            //
            // The string Value needs to be readjusted. Use ReadValue as a temporary
            // buffer

            memset( ReadValue, 0, 2*MAX_VALUE_LENGTH );
            {
                UCHAR CanCopy  = 0x0;
                BOOL  IgnoreNext = FALSE;

                for( i = 0, j = 0 ; i < (INT)strlen( Value ); i++ ) 
                {
                    if ( ( Value[i] == '"' ) && ( IgnoreNext == FALSE ) ) 
                    {
                        CanCopy = ~CanCopy;
                        if ( !CanCopy ) 
                        {
                            ReadValue[j++] = '\0';
                        }
                    }
                    if (  Value[i] == '\\' ) 
                    {
                        IgnoreNext = TRUE;
                    } 
                    else 
                    {
                        IgnoreNext = FALSE;
                    }
                    if ( CanCopy ) 
                    {
                        ReadValue[j++] = Value[i];
                    }
                }
            }

            //
            // Fill the 2 nulls at the end of the array
            //

            ReadValue[j++] = '\0';ReadValue[j++] = '\0';
            ValueSize = j;
            memcpy( Value, ReadValue, j );
            memset( ReadValue, 0, 2*MAX_VALUE_LENGTH );
            break;

        case NONE :
            ValueType = REG_NONE;
            ValueSize = strlen( Value );
            break;

        case RESOURCE_LIST :
            ValueType = REG_RESOURCE_LIST;
            ValueSize = strlen( Value );
            break;

        case SZ :
            ValueType = REG_SZ;
            ValueSize = strlen( Value );
            break;

        default : 
            break;

    }

    //
    // Switch to the demanded operation
    //

    switch ( CmdArgs->ARGS.REGISTRY_ENTRY.OperationType ) 
    {
        case ADD_KEY:
            TmpBuf += (BYTE)sprintf( TmpBuf, "\tSubCommandCode = ADD_KEY\n" );

            SamDesired = KEY_ALL_ACCESS;

            Status = RegCreateKeyEx( DbaseHKey, SubKeyName, (DWORD)0,
                                     ClassName, REG_OPTION_NON_VOLATILE,
                                     SamDesired, NULL, &KeyHandle, &Disposition );

            if ( Status != ERROR_SUCCESS ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf, "\tStatus         = %ldL\n", Status );
                TmpBuf += (BYTE)sprintf( TmpBuf,
                "\n\tTpctl: Unable to create\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                       SubKeyName, ClassName, DbaseName );
                break;

            }

            TmpBuf += (BYTE)sprintf( TmpBuf, "\tStatus         = SUCCESS\n" );
            TmpBuf += (BYTE)sprintf( TmpBuf, "\tDisposition    = " );

            if ( Disposition == REG_CREATED_NEW_KEY ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf, "CREATED A NEW KEY\n" );
            } 
            else 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf, "KEY ALREADY EXISTS\n" );
            }
            break;

        case DELETE_KEY:
            TmpBuf += (BYTE)sprintf( TmpBuf, "\tSubCommandCode = DELETE_KEY\n" );

            Status = RegDeleteKey( DbaseHKey, SubKeyName );
            if ( Status != ERROR_SUCCESS ) {

                TmpBuf += (BYTE)sprintf( TmpBuf, "\tStatus         = %ldL\n", Status );
                TmpBuf += (BYTE)sprintf( TmpBuf,
                "\n\tTpctl: Unable to delete\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                       SubKeyName, ClassName, DbaseName );
                 break;

            }

            TmpBuf += (BYTE)sprintf( TmpBuf, "\tStatus         = SUCCESS\n" );
            break;


        case QUERY_KEY:

            CompleteQueryStatus = TRUE;

            TmpBuf += (BYTE)sprintf( TmpBuf, "\tSubCommandCode = QUERY_KEY\n" );

            //
            // Open the Registry Key
            //

            SamDesired = KEY_READ;

            Status = RegOpenKeyEx( DbaseHKey, SubKeyName, (DWORD)0, SamDesired, &KeyHandle );
            if ( Status != ERROR_SUCCESS ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf, "\tStatus         = %ldL\n", Status );
                TmpBuf += (BYTE)sprintf( TmpBuf,
                "\n\tTpctl: Unable to open\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                       SubKeyName, ClassName, DbaseName );
                 break;

            }

            {
                LPSTR       TmpKeyClassName = NULL, TmpSubKeyName   = NULL, TmpValueName = NULL;
                DWORD       NumberOfSubKeys, NumberOfValues, TmpValueType, ClassNameSize;
                DWORD       TmpDwordVar, LongestSubKeyNameSize, LongestSubKeyClassNameSize;
                DWORD       LongestValueNameSize;
                FILETIME    LastWriteTime;
                SYSTEMTIME  SystemTime;
                CHAR        *DayOfWeek[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
                                           "Friday", "Saturday" };

                if ( (TmpKeyClassName = calloc( 1, MAX_PATH+1 )) == NULL ) 
                {
                    TpctlErrorLog( 
        "\n\tTpctl: TpctlPeformRegistryOperation: QueryKey unable to allocate memory resources\n", 
                                    NULL );
                    return;
                }

                ClassNameSize = MAX_PATH+1;
                Status = RegQueryInfoKey( KeyHandle, TmpKeyClassName, &ClassNameSize,
                                          NULL, &NumberOfSubKeys, &LongestSubKeyNameSize,
                                          &LongestSubKeyClassNameSize, &NumberOfValues, 
                                          &LongestValueNameSize,
                                          &TmpDwordVar, &TmpDwordVar, &LastWriteTime );

                if ( (Status == ERROR_MORE_DATA) || (Status == ERROR_INSUFFICIENT_BUFFER) ) 
                {
                    free( TmpKeyClassName );
                    if ( (TmpKeyClassName = calloc( 1, ClassNameSize+2 )) == NULL ) 
                    {
                        TpctlErrorLog( "\n\tTpctl: TpctlPeformRegistryOperation: QueryKey unable to allocate memory resources\n", NULL );
                        return;
                    }
                    Status = RegQueryInfoKey( KeyHandle, TmpKeyClassName, &ClassNameSize,
                                              NULL, &NumberOfSubKeys, &LongestSubKeyNameSize,
                                              &LongestSubKeyClassNameSize, &NumberOfValues, 
                                              &LongestValueNameSize,
                                              &TmpDwordVar, &TmpDwordVar, &LastWriteTime );
                }
                if ( Status != ERROR_SUCCESS ) 
                {
                    TmpBuf += (BYTE)sprintf( TmpBuf,"\tStatus         = %ldL\n", Status );
                    TmpBuf += (BYTE)sprintf( TmpBuf,
          "\n\tTpctl: Unable to QueryInfo on\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                           SubKeyName, ClassName, DbaseName );
                     break;
                }

                TmpBuf += sprintf( TmpBuf, 
                    "\tKey Class Name = %s\n\tNumber Of SubKeys = %ld\n\tNumber of Values  = %ld\n",
                                        TmpKeyClassName, NumberOfSubKeys, NumberOfValues );

                if ( FileTimeToSystemTime( &LastWriteTime, &SystemTime ) ) 
                {
                    TmpBuf += sprintf( TmpBuf, 
                            "\tLast Write Time   = %s %2.2d-%2.2d-%4.4d at %2.2d:%2.2d:%2.2d %s\n",
                                        DayOfWeek[SystemTime.wDayOfWeek],
                                        SystemTime.wMonth,
                                        SystemTime.wDay,
                                        SystemTime.wYear,
                                        ((SystemTime.wHour > 12) ? (SystemTime.wHour-12) 
                                                                 : SystemTime.wHour),
                                        SystemTime.wMinute,
                                        SystemTime.wSecond,
                                        ((SystemTime.wHour > 12) ? "AM" : "PM") );
                } 
                else 
                {
                    TmpBuf += sprintf( TmpBuf, "\tLast Write Time   = Undefined\n" );
                }

                free( TmpKeyClassName );

                if ( (TmpSubKeyName = calloc( 1, LongestSubKeyNameSize+2 )) == NULL ) 
                {
                    TpctlErrorLog( "\n\tTpctl: TpctlPeformRegistryOperation: QueryKey unable to allocate memory resources\n", NULL );
                    return;
                }

                TmpBuf += sprintf( TmpBuf, "\tSub Key Name(s)\n" );

                for( i = 0; i < (INT)NumberOfSubKeys; i++ ) 
                {
                    memset( TmpSubKeyName, 0, LongestSubKeyNameSize+2 );
                    Status = RegEnumKey( KeyHandle, i, TmpSubKeyName, LongestSubKeyNameSize+2 );
                    if ( Status != ERROR_SUCCESS ) 
                    {
                        TmpBuf += (BYTE)sprintf( TmpBuf,"\tStatus         = %ldL\n", Status );
                        TmpBuf += (BYTE)sprintf( TmpBuf,
                                  "\n\tTpctl: Unable to Enumerate Key Index %d from\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                               i, SubKeyName, ClassName, DbaseName );
                        CompleteQueryStatus = FALSE;
                    } 
                    else 
                    {
                        TmpBuf += sprintf( TmpBuf, "\t%2d.\t%s\n", i, TmpSubKeyName );
                    }
                }

                free( TmpSubKeyName );

                if ( (TmpValueName = calloc( 1, LongestValueNameSize+2 )) == NULL ) 
                {
                    TpctlErrorLog( "\n\tTpctl: TpctlPeformRegistryOperation: QueryKey unable to allocate memory resources\n", NULL );
                    return;
                }

                TmpBuf += sprintf( TmpBuf, "\tSub Key Value Name(s) and Associated Type(s)\n" );

                for( i = 0; i < (INT)NumberOfValues; i++ ) 
                {
                    memset( TmpValueName, 0, LongestValueNameSize+2 );
                    TmpDwordVar = LongestValueNameSize+2;
                    Status = RegEnumValue( KeyHandle, i, TmpValueName, &TmpDwordVar, NULL,  
                                           &TmpValueType, NULL, NULL );
                    if ( Status != ERROR_SUCCESS ) 
                    {
                        TmpBuf += (BYTE)sprintf( TmpBuf,"\tStatus         = %ldL\n", Status );
                        TmpBuf += (BYTE)sprintf( TmpBuf,
                                  "\n\tTpctl: Unable to Enumerate Value Index %d from\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                               i, SubKeyName, ClassName, DbaseName );
                        CompleteQueryStatus = FALSE;
                    } 
                    else 
                    {
                        TmpBuf += sprintf( TmpBuf, 
                    "\t%2d.\t%-30s%-15s\n", i, TmpValueName, TpctlGetValueType( TmpValueType ) );

                    }
                }

                free( TmpValueName );

            }

            if ( CompleteQueryStatus ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf, "\tComplete Query Status = SUCCESS\n" );
            } 
            else 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf, "\tComplete Query Status = FAILURE\n" );
            }
            break;

        case ADD_VALUE:
        case CHANGE_VALUE:
            if ( CmdArgs->ARGS.REGISTRY_ENTRY.OperationType == CHANGE_VALUE ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf, "\tSubCommandCode = CHANGE_VALUE\n" );
                SamDesired = KEY_WRITE|KEY_READ;
            } 
            else 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf, "\tSubCommandCode = ADD_VALUE\n" );
                SamDesired = KEY_ALL_ACCESS;
            }


            Status = RegOpenKeyEx( DbaseHKey, SubKeyName, (DWORD)0, SamDesired, &KeyHandle );
            if ( Status != ERROR_SUCCESS ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf,"\tStatus         = %ldL\n", Status );
                TmpBuf += (BYTE)sprintf( TmpBuf,
                "\n\tTpctl: Unable to open\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                       SubKeyName, ClassName, DbaseName );
                break;

            }

            //
            // If this is a request to change a value, make sure that the value exists
            //

            if ( CmdArgs->ARGS.REGISTRY_ENTRY.OperationType == CHANGE_VALUE ) 
            {
                //
                // Make sure the ValueName exist since this is a change request
                //
                ReadValueSize = 2*MAX_VALUE_LENGTH;
                ReadValueType = ValueType;
                Status = RegQueryValueEx( KeyHandle, ValueName, (DWORD)0, &ReadValueType, 
                                          ReadValue, &ReadValueSize );
                if ( (Status != ERROR_SUCCESS) && 
                     (Status != ERROR_MORE_DATA) && 
                     (Status != ERROR_INSUFFICIENT_BUFFER) ) 
                {
                    TmpBuf += (BYTE)sprintf( TmpBuf,"\tStatus         = %ldL\n", Status );
                    TmpBuf += (BYTE)sprintf( TmpBuf,
"\n\tTpctl: Unable to access\n\tValue    : %s\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                           ValueName, SubKeyName, ClassName, DbaseName );
                    break;

                }
            }

            //
            // Now set the values as expected
            //
            Status = RegSetValueEx( KeyHandle, ValueName, (DWORD)0, ValueType, Value, ValueSize );
            if ( Status != ERROR_SUCCESS ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf,"\tStatus         = %ldL\n", Status );
                TmpBuf += (BYTE)sprintf( TmpBuf,
"\n\tTpctl: Unable to change\n\tValue    : %s\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                       ValueName, SubKeyName, ClassName, DbaseName );
                break;

            }
            TmpBuf += (BYTE)sprintf( TmpBuf, "\tStatus         = SUCCESS\n" );
            break;


        case DELETE_VALUE:
            TmpBuf += (BYTE)sprintf( TmpBuf, "\tSubCommandCode = DELETE_VALUE\n" );

            //
            // Open the Registry Key
            //
            SamDesired = KEY_SET_VALUE;

            Status = RegOpenKeyEx( DbaseHKey, SubKeyName, (DWORD)0, SamDesired, &KeyHandle );
            if ( Status != ERROR_SUCCESS ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf,"\tStatus         = %ldL\n", Status );
                TmpBuf += (BYTE)sprintf( TmpBuf,
                "\n\tTpctl: Unable to open\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                       SubKeyName, ClassName, DbaseName );
                break;

            }

            Status = RegDeleteValue( KeyHandle, ValueName );
            if ( Status != ERROR_SUCCESS ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf,"\tStatus         = %ldL\n", Status );
                TmpBuf += (BYTE)sprintf( TmpBuf,
"\n\tTpctl: Unable to delete\n\tValue    : %s\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                       ValueName, SubKeyName, ClassName, DbaseName );
                break;

            }

            TmpBuf += (BYTE)sprintf( TmpBuf, "\tStatus         = SUCCESS\n" );
            break;

        case QUERY_VALUE:
            TmpBuf += (BYTE)sprintf( TmpBuf, "\tSubCommandCode = QUERY_VALUE\n" );

            //
            // Open the Registry Key
            //
            SamDesired = KEY_QUERY_VALUE;

            Status = RegOpenKeyEx( DbaseHKey, SubKeyName, (DWORD)0, SamDesired, &KeyHandle );
            if ( Status != ERROR_SUCCESS ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf,"\tStatus         = %ldL\n", Status );
                TmpBuf += (BYTE)sprintf( TmpBuf,
                "\n\tTpctl: Unable to open\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                       SubKeyName, ClassName, DbaseName );
                 break;
            }

            //
            // Make sure the ValueName exist since this is a change request
            //

            ReadValueSize = 2*MAX_VALUE_LENGTH;
            Status = RegQueryValueEx( KeyHandle, ValueName, (DWORD)0, &ReadValueType, 
                                      ReadValue, &ReadValueSize );

            if ( (Status == ERROR_MORE_DATA) || (Status == ERROR_INSUFFICIENT_BUFFER) ) 
            {
                free( ReadValue );
                ReadValue = NULL;
                ReadValue = calloc( 1, ReadValueSize+1 );
                if ( ReadValue == NULL ) 
                {
                    TpctlErrorLog( 
        "\n\tTpctl: TpctlPeformRegistryOperation: QueryValue unable to allocate memory resources\n",
                                  NULL );
                    return;
                }
                Status = RegQueryValueEx( KeyHandle, ValueName, (DWORD)0, &ReadValueType, 
                                          ReadValue, &ReadValueSize );
            }

            if ( Status != ERROR_SUCCESS ) 
            {
                TmpBuf += (BYTE)sprintf( TmpBuf,"\tStatus         = %ldL\n", Status );
                TmpBuf += (BYTE)sprintf( TmpBuf,
"\n\tTpctl: Unable to access\n\tValue    : %s\n\tSubkey   : %s\n\tClassName: %s\n\tDatabase : %s\n",
                                       ValueName, SubKeyName, ClassName, DbaseName );
                break;

            }

            TmpBuf += (BYTE)sprintf( TmpBuf, "\tStatus         = SUCCESS\n" );
            TmpBuf = TpctlEnumerateRegistryInfo( TmpBuf, DbaseName, SubKeyName, ValueName,
                                               ReadValueType, ReadValue, ReadValueSize );
            break;


        default: 
            break;

    }

    //
    // Close any open keys and deallocate any allocated resources
    //

    RegCloseKey( KeyHandle );
    free( ReadValue );


    //
    // Print the buffer
    //

    if ( Verbose ) 
    {
        if ( !WriteFile(GetStdHandle( STD_OUTPUT_HANDLE ),
                        GlobalBuf,
                        TmpBuf-GlobalBuf,
                        &BytesWritten,
                        NULL )) 
        {
            Status = GetLastError();
            TpctlErrorLog("\n\tTpctl: WriteFile to screen failed, returned 0x%lx\n",(PVOID)Status);
        }
    }

    if ( CommandsFromScript ) 
    {
        if ( !WriteFile(Scripts[ScriptIndex].LogHandle,
                        GlobalBuf,
                        TmpBuf-GlobalBuf,
                        &BytesWritten,
                        NULL )) 
        {
            Status = GetLastError();
            TpctlErrorLog("\n\tTpctl: WriteFile to logfile failed, returned 0x%lx\n",(PVOID)Status);
        }

    } 
    else if ( CommandLineLogging ) 
    {
        if ( !WriteFile(CommandLineLogHandle,
                        GlobalBuf,
                        TmpBuf-GlobalBuf,
                        &BytesWritten,
                        NULL )) 
        {
            Status = GetLastError();
            TpctlErrorLog("\n\tTpctl: WriteFile to logfile failed, returned 0x%lx\n",(PVOID)Status);
        }
    }

    //
    // Free up resources
    //
    free( ReadValue );

}



BOOL
TpctlInitCommandBuffer(
    OUT PCMD_ARGS CmdArgs,
    IN DWORD CmdCode
    )

// -------------------
// 
// Routine Description:
// 
//     Initialize the cmd buffer to be passed to the driver with the arguments
//     read from the command line or the script file.
// 
// Arguments:
// 
//     CmdArgs - The buffer to store the arguments in.
// 
//     CmdCode - The command that is being issued, and therefore the command
//               to write the arguments for into the buffer.
// Return Value:
// 
//     BOOL - TRUE if the OpenInstance is valid and all the arguments are
//            written to the buffer, FALSE otherwise.
// 
// ----------------

{
    LPBYTE p, q, s, t;
    DWORD i, j;
    DWORD OidIndex;

    //
    // If the OpenInstance is invalid return immediately.
    //
    switch ( CmdCode ) 
    {
        case SETENV      :
        case GO          :
        case PAUSE       :
        case OPEN        :
        case CLOSE       :
        case QUERYINFO   :
        case SETPF       :
        case SETLA       :
        case ADDMA       :
        case DELMA       :
        case SETFA       :
        case SETGA       :
        case SETINFO     :
        case RESET       :
        case STOPSEND    :
        case WAITSEND    :
        case RECEIVE     :
        case STOPREC     :
        case GETEVENTS   :
        case STRESSSERVER:
        case ENDSTRESS   :
        case WAITSTRESS  :
        case CHECKSTRESS :
        case SEND        :
        case STRESS      :
        case PERFSERVER:
        case PERFCLIENT: 
            if (( GlobalCmdArgs.OpenInstance < 1 ) ||
                ( GlobalCmdArgs.OpenInstance > NUM_OPEN_INSTANCES )) 
            {
                TpctlErrorLog("\n\tTpctl: %d not a valid Open Instance Value ",
                              (PVOID)GlobalCmdArgs.OpenInstance);
                TpctlErrorLog("(1-%d).\n", (PVOID)NUM_OPEN_INSTANCES);
                return FALSE;
            }

        default: 
            break;

    }


    //
    // Otherwise let's stuff the arguments into the buffer.
    //

    CmdArgs->CmdCode = CmdCode;
    CmdArgs->OpenInstance = GlobalCmdArgs.OpenInstance;

    //
    // Now do the command dependant stuff.
    //

    switch( CmdCode ) 
    {
        case SETENV:

            CmdArgs->ARGS.ENV.WindowSize =
                GlobalCmdArgs.ARGS.ENV.WindowSize;

            CmdArgs->ARGS.ENV.RandomBufferNumber =
                GlobalCmdArgs.ARGS.ENV.RandomBufferNumber;

            CmdArgs->ARGS.ENV.StressDelayInterval =
                GlobalCmdArgs.ARGS.ENV.StressDelayInterval;

            CmdArgs->ARGS.ENV.UpForAirDelay =
                GlobalCmdArgs.ARGS.ENV.UpForAirDelay;

            CmdArgs->ARGS.ENV.StandardDelay =
                GlobalCmdArgs.ARGS.ENV.StandardDelay;

            p = CmdArgs->ARGS.ENV.StressAddress;
            q = GlobalCmdArgs.ARGS.ENV.StressAddress;

            s = CmdArgs->ARGS.ENV.ResendAddress;
            t = GlobalCmdArgs.ARGS.ENV.ResendAddress;

            for( i=0;i<ADDRESS_LENGTH;i++ ) 
            {
                *p++ = *q++;
                *s++ = *t++;
            }
            break;

        case BEGINLOGGING:
            strcpy( CmdArgs->ARGS.FILES.LogFile,GlobalCmdArgs.ARGS.FILES.LogFile );
            break;

        case RECORDINGENABLE:
            strcpy( CmdArgs->ARGS.RECORD.ScriptFile,GlobalCmdArgs.ARGS.RECORD.ScriptFile );
            break;

        case GO:
        case PAUSE:
            p = CmdArgs->ARGS.PAUSE_GO.RemoteAddress;
            q = GlobalCmdArgs.ARGS.PAUSE_GO.RemoteAddress;

            for( i=0;i<ADDRESS_LENGTH;i++ ) 
            {
                *p++ = *q++;
            }

            CmdArgs->ARGS.PAUSE_GO.TestSignature =
                GlobalCmdArgs.ARGS.PAUSE_GO.TestSignature;

            srand(TpctlSeed);
            CmdArgs->ARGS.PAUSE_GO.UniqueSignature = TpctlSeed = rand();
            break;

        case OPEN:
            strcpy( CmdArgs->ARGS.OPEN_ADAPTER.AdapterName,
                    GlobalCmdArgs.ARGS.OPEN_ADAPTER.AdapterName );
            CmdArgs->ARGS.OPEN_ADAPTER.NoArcNet = 0;
            if (getenv( "NOARCNET" ))
            {
                CmdArgs->ARGS.OPEN_ADAPTER.NoArcNet = 1;
            }
            break;

        case QUERYINFO:
            OidIndex = TpLookUpOidInfo( GlobalCmdArgs.ARGS.TPQUERY.OID );

            if (( OidIndex == -1 ) || ( OidArray[OidIndex].QueryInfo != TRUE )) 
            {
                TpctlErrorLog("\n\tTpctl: 0x%08lX not a valid NdisRequestQueryInformation OID.\n",
                    (PVOID)GlobalCmdArgs.ARGS.TPQUERY.OID);
                return FALSE;
            }
            CmdArgs->ARGS.TPQUERY.OID = GlobalCmdArgs.ARGS.TPQUERY.OID;
            break;

        case SETPF:
        case SETLA:
        case ADDMA:
        case SETFA:
        case SETGA:
        case SETINFO:
            CmdArgs->ARGS.TPSET.OID = 0x0;

            //
            // Sanjeevk: Performed a scrub on the multiple if. Bug #5203
            //

            switch ( CmdCode ) 
            {
                case SETINFO: 
                    CmdArgs->ARGS.TPSET.OID = GlobalCmdArgs.ARGS.TPSET.OID;
                    break;
    
                case SETPF: 
                    CmdArgs->ARGS.TPSET.OID = OID_GEN_CURRENT_PACKET_FILTER;
                    break;

                case SETLA: 
                    CmdArgs->ARGS.TPSET.OID = OID_GEN_CURRENT_LOOKAHEAD;
                    break;
                case ADDMA: 
                    if ( Open[CmdArgs->OpenInstance-1].MediumType == NdisMedium802_3 ) 
                    {
                        CmdArgs->ARGS.TPSET.OID = OID_802_3_MULTICAST_LIST;
                    } 
                    else 
                    {
                        //
                        // Only FDDI and 802.3 permit multicast addressing. Since the
                        // medium is not 802.3, it must be FDDI
                        //
                        CmdArgs->ARGS.TPSET.OID = OID_FDDI_LONG_MULTICAST_LIST;
                    }
                    break;

                case SETFA: 
                    CmdArgs->ARGS.TPSET.OID = OID_802_5_CURRENT_FUNCTIONAL;
                    break;

                case SETGA: 
                    CmdArgs->ARGS.TPSET.OID = OID_802_5_CURRENT_GROUP;
                    break;

                default: 
                    break;
            }

            switch ( CmdArgs->ARGS.TPSET.OID ) 
            {
                case OID_GEN_CURRENT_PACKET_FILTER:
                    CmdArgs->ARGS.TPSET.U.PacketFilter =
                        GlobalCmdArgs.ARGS.TPSET.U.PacketFilter;
                    break;

                case OID_GEN_CURRENT_LOOKAHEAD:
                    CmdArgs->ARGS.TPSET.U.LookaheadSize =
                        GlobalCmdArgs.ARGS.TPSET.U.LookaheadSize;
                    break;

                case OID_802_3_MULTICAST_LIST:
                case OID_FDDI_LONG_MULTICAST_LIST:
                {
                    PMULT_ADDR NextMultAddr;
                    DWORD OI = GlobalCmdArgs.OpenInstance - 1;

                    p = CmdArgs->ARGS.TPSET.U.MulticastAddress[0];
                    q = GlobalCmdArgs.ARGS.TPSET.U.MulticastAddress[0];

                    for ( i=0;i<ADDRESS_LENGTH;i++ ) 
                    {
                        *p++ = *q++;
                    }

                    NextMultAddr = Open[OI].MulticastAddresses;

                    //
                    // XXX: Should the stress tests be required to add and
                    // delete the stress multicast address to/from this list?
                    //

                    j = 1;

                    while ( NextMultAddr != NULL ) 
                    {
                        p = CmdArgs->ARGS.TPSET.U.MulticastAddress[j++];

                        for ( i=0;i<ADDRESS_LENGTH;i++ ) 
                        {
                            *p++ = NextMultAddr->MulticastAddress[i];
                        }

                        NextMultAddr = NextMultAddr->Next;
                    }
                    CmdArgs->ARGS.TPSET.NumberMultAddrs = Open[OI].NumberMultAddrs + 1;
                    break;
                }

                case OID_802_5_CURRENT_FUNCTIONAL:
                case OID_802_5_CURRENT_GROUP:
                    p = CmdArgs->ARGS.TPSET.U.FunctionalAddress;
                    q = GlobalCmdArgs.ARGS.TPSET.U.FunctionalAddress;

                    for ( i=0;i<FUNCTIONAL_ADDRESS_LENGTH;i++ ) 
                    {
                        *p++ = *q++;
                    }
                    break;

                default:
                    TpctlErrorLog("\n\tTpctl: 0x%08lX not a valid NdisRequestSetInformation OID.\n",
                                        (PVOID)GlobalCmdArgs.ARGS.TPSET.OID);
                    return FALSE;
            }
            break;

        case DELMA:
        {
            PMULT_ADDR NextMultAddr;
            DWORD OI = CmdArgs->OpenInstance - 1;
            BOOL AddressFound = FALSE;

            j = 0;

            //
            // Copy the addresses that do not match the one to be deleted into
            // the multicast list buffer to be reset.
            //

            //
            // Sanjeevk: Another change point. Bug #5203
            //
            if ( Open[CmdArgs->OpenInstance-1].MediumType == NdisMedium802_3 ) 
            {
                CmdArgs->ARGS.TPSET.OID = OID_802_3_MULTICAST_LIST;
            } 
            else 
            {
                //
                // Only FDDI and 802.3 permit multicast addressing. Since the
                // medium is not 802.3, it must be FDDI
                //
                CmdArgs->ARGS.TPSET.OID = OID_FDDI_LONG_MULTICAST_LIST;
            }

            CmdArgs->ARGS.TPSET.NumberMultAddrs = 0;
            NextMultAddr = Open[OI].MulticastAddresses;

            while ( NextMultAddr != NULL ) 
            {
                if ( memcmp(GlobalCmdArgs.ARGS.TPSET.U.MulticastAddress[0],
                            NextMultAddr->MulticastAddress,
                            ADDRESS_LENGTH) != 0 ) 
                {
                    p = CmdArgs->ARGS.TPSET.U.MulticastAddress[j++];

                    for ( i=0;i<ADDRESS_LENGTH;i++ ) 
                    {
                        *p++ = NextMultAddr->MulticastAddress[i];
                    }

                    CmdArgs->ARGS.TPSET.NumberMultAddrs++;

                } 
                else 
                {
                    AddressFound = TRUE;
                }

                NextMultAddr = NextMultAddr->Next;
            }

            if ( AddressFound == FALSE ) 
            {
                TpctlErrorLog("\n\tTpctl: The multicast address %02X",
                    (PVOID)GlobalCmdArgs.ARGS.TPSET.U.MulticastAddress[0][0]);
                TpctlErrorLog("-%02X",
                    (PVOID)GlobalCmdArgs.ARGS.TPSET.U.MulticastAddress[0][1]);
                TpctlErrorLog("-%02X",
                    (PVOID)GlobalCmdArgs.ARGS.TPSET.U.MulticastAddress[0][2]);
                TpctlErrorLog("-%02X",
                    (PVOID)GlobalCmdArgs.ARGS.TPSET.U.MulticastAddress[0][3]);
                TpctlErrorLog("-%02X",
                    (PVOID)GlobalCmdArgs.ARGS.TPSET.U.MulticastAddress[0][4]);
                TpctlErrorLog("-%02X has not been added.\n",
                    (PVOID)GlobalCmdArgs.ARGS.TPSET.U.MulticastAddress[0][5]);

                //
                // We will let the call go thru since we expect the driver to agree
                // with our findings which is the MA which is being deleted is not present
                //
                //
            }
            break;
        }

        case SEND:
            p = CmdArgs->ARGS.TPSEND.DestAddress;
            q = GlobalCmdArgs.ARGS.TPSEND.DestAddress;
            s = CmdArgs->ARGS.TPSEND.ResendAddress;
            t = GlobalCmdArgs.ARGS.TPSEND.ResendAddress;

            for ( i=0;i<ADDRESS_LENGTH;i++ ) 
            {
                *p++ = *q++;
                *s++ = *t++;
            }

            CmdArgs->ARGS.TPSEND.PacketSize =
                GlobalCmdArgs.ARGS.TPSEND.PacketSize;

            CmdArgs->ARGS.TPSEND.NumberOfPackets =
                GlobalCmdArgs.ARGS.TPSEND.NumberOfPackets;

            break;

        case STRESS:

            CmdArgs->ARGS.TPSTRESS.MemberType =
                GlobalCmdArgs.ARGS.TPSTRESS.MemberType;

            CmdArgs->ARGS.TPSTRESS.PacketType =
                GlobalCmdArgs.ARGS.TPSTRESS.PacketType;

            CmdArgs->ARGS.TPSTRESS.PacketSize =
                GlobalCmdArgs.ARGS.TPSTRESS.PacketSize;

            CmdArgs->ARGS.TPSTRESS.PacketMakeUp =
                GlobalCmdArgs.ARGS.TPSTRESS.PacketMakeUp;

            CmdArgs->ARGS.TPSTRESS.ResponseType =
                GlobalCmdArgs.ARGS.TPSTRESS.ResponseType;

            CmdArgs->ARGS.TPSTRESS.DelayType =
                GlobalCmdArgs.ARGS.TPSTRESS.DelayType;

            CmdArgs->ARGS.TPSTRESS.DelayLength =
                GlobalCmdArgs.ARGS.TPSTRESS.DelayLength;

            CmdArgs->ARGS.TPSTRESS.TotalIterations =
                GlobalCmdArgs.ARGS.TPSTRESS.TotalIterations;

            CmdArgs->ARGS.TPSTRESS.TotalPackets =
                GlobalCmdArgs.ARGS.TPSTRESS.TotalPackets;

            CmdArgs->ARGS.TPSTRESS.WindowEnabled =
                GlobalCmdArgs.ARGS.TPSTRESS.WindowEnabled;

            CmdArgs->ARGS.TPSTRESS.DataChecking =
                GlobalCmdArgs.ARGS.TPSTRESS.DataChecking;

            CmdArgs->ARGS.TPSTRESS.PacketsFromPool =
                GlobalCmdArgs.ARGS.TPSTRESS.PacketsFromPool;

            break;


        case REGISTRY :
            CmdArgs->ARGS.REGISTRY_ENTRY.OperationType = 
                GlobalCmdArgs.ARGS.REGISTRY_ENTRY.OperationType ;
            CmdArgs->ARGS.REGISTRY_ENTRY.KeyDatabase   = 
                GlobalCmdArgs.ARGS.REGISTRY_ENTRY.KeyDatabase   ;
            CmdArgs->ARGS.REGISTRY_ENTRY.ValueType     = 
                GlobalCmdArgs.ARGS.REGISTRY_ENTRY.ValueType     ;

            strcpy( CmdArgs->ARGS.REGISTRY_ENTRY.SubKey      , 
                    GlobalCmdArgs.ARGS.REGISTRY_ENTRY.SubKey );
            strcpy( CmdArgs->ARGS.REGISTRY_ENTRY.SubKeyClass , 
                    GlobalCmdArgs.ARGS.REGISTRY_ENTRY.SubKeyClass );

            strcpy( CmdArgs->ARGS.REGISTRY_ENTRY.SubKeyValueName, 
                    GlobalCmdArgs.ARGS.REGISTRY_ENTRY.SubKeyValueName );
            strcpy( CmdArgs->ARGS.REGISTRY_ENTRY.SubKeyValue, 
                    GlobalCmdArgs.ARGS.REGISTRY_ENTRY.SubKeyValue );

            break;


        case PERFCLIENT:
            p = CmdArgs->ARGS.TPPERF.PerfServerAddr;
            q = GlobalCmdArgs.ARGS.TPPERF.PerfServerAddr;
            s = CmdArgs->ARGS.TPPERF.PerfSendAddr;
            t = GlobalCmdArgs.ARGS.TPPERF.PerfSendAddr;

            for ( i=0;i<ADDRESS_LENGTH;i++ ) 
            {
                *p++ = *q++;
                *s++ = *t++;
            }
            CmdArgs->ARGS.TPPERF.PerfPacketSize = GlobalCmdArgs.ARGS.TPPERF.PerfPacketSize;
            CmdArgs->ARGS.TPPERF.PerfNumPackets = GlobalCmdArgs.ARGS.TPPERF.PerfNumPackets;
            CmdArgs->ARGS.TPPERF.PerfDelay = GlobalCmdArgs.ARGS.TPPERF.PerfDelay;
            CmdArgs->ARGS.TPPERF.PerfMode = GlobalCmdArgs.ARGS.TPPERF.PerfMode;
            break;

        case CLOSE:
        case RESET:
        case STOPSEND:
        case WAITSEND:
        case RECEIVE:
        case STOPREC:
        case GETEVENTS:
        case STRESSSERVER:
        case ENDSTRESS:
        case WAITSTRESS:
        case CHECKSTRESS:
        case WAIT:
        case VERBOSE:
        case BREAKPOINT:
        case QUIT:
        case HELP:
        case SHELL:
        case RECORDINGDISABLE:
        case DISABLE:
        case ENABLE:
        case PERFSERVER:
            break;

        default:
            TpctlErrorLog("TpctlInitCommandBuffer: Invalid Command code.\n",NULL);
            break;

    } // switch();

    return TRUE;
}



LPSTR
TpctlGetEventType(
    TP_EVENT_TYPE TpEventType
    )
{
    static TP_EVENT_TYPE Event[] = {
        CompleteOpen,
        CompleteClose,
        CompleteSend,
        CompleteTransferData,
        CompleteReset,
        CompleteRequest,
        IndicateReceive,
        IndicateReceiveComplete,
        IndicateStatus,
        IndicateStatusComplete,
        Unknown
    };

#define EventCount (sizeof(Event)/sizeof(TP_EVENT_TYPE))

    static LPSTR EventString[] = {  // BUGUBUG Add new events open close...
        "NdisCompleteOpen",
        "NdisCompleteClose",
        "NdisCompleteSend",
        "NdisCompleteTransferData",
        "NdisCompleteReset",
        "NdisCompleteRequest",
        "NdisIndicateReceive",
        "NdisIndicateReceiveComplete",
        "NdisIndicateStatus",
        "NdisIndicateStatusComplete",
        "Unknown Function"
    };

    static BYTE BadEvent[] = "UNDEFINED";
    DWORD i;


    for (i=0; i<EventCount; i++) 
    {
        if (TpEventType == Event[i]) 
        {
            return EventString[i];
        }
    }

    return BadEvent;

#undef StatusCount
}



LPSTR
TpctlGetStatus(
    NDIS_STATUS GeneralStatus
    )
{

    static NDIS_STATUS Status[] = {
        NDIS_STATUS_SUCCESS,
        NDIS_STATUS_PENDING,
        NDIS_STATUS_NOT_RECOGNIZED,
        NDIS_STATUS_NOT_COPIED,
        NDIS_STATUS_ONLINE,
        NDIS_STATUS_RESET_START,
        NDIS_STATUS_RESET_END,
        NDIS_STATUS_RING_STATUS,
        NDIS_STATUS_CLOSED,

        NDIS_STATUS_WAN_LINE_UP,
        NDIS_STATUS_WAN_LINE_DOWN,
        NDIS_STATUS_WAN_FRAGMENT,

        NDIS_STATUS_NOT_RESETTABLE,
        NDIS_STATUS_SOFT_ERRORS,
        NDIS_STATUS_HARD_ERRORS,
        NDIS_STATUS_FAILURE,
        NDIS_STATUS_RESOURCES,
        NDIS_STATUS_CLOSING,
        NDIS_STATUS_BAD_VERSION,
        NDIS_STATUS_BAD_CHARACTERISTICS,
        NDIS_STATUS_ADAPTER_NOT_FOUND,
        NDIS_STATUS_OPEN_FAILED,
        NDIS_STATUS_DEVICE_FAILED,
        NDIS_STATUS_MULTICAST_FULL,
        NDIS_STATUS_MULTICAST_EXISTS,
        NDIS_STATUS_MULTICAST_NOT_FOUND,
        NDIS_STATUS_REQUEST_ABORTED,
        NDIS_STATUS_RESET_IN_PROGRESS,
        NDIS_STATUS_CLOSING_INDICATING,
        NDIS_STATUS_NOT_SUPPORTED,
        NDIS_STATUS_INVALID_PACKET,
        NDIS_STATUS_OPEN_LIST_FULL,
        NDIS_STATUS_ADAPTER_NOT_READY,
        NDIS_STATUS_ADAPTER_NOT_OPEN,
        NDIS_STATUS_NOT_INDICATING,
        NDIS_STATUS_INVALID_LENGTH,
        NDIS_STATUS_INVALID_DATA,
        NDIS_STATUS_BUFFER_TOO_SHORT,
        NDIS_STATUS_INVALID_OID,
        NDIS_STATUS_ADAPTER_REMOVED,
        NDIS_STATUS_UNSUPPORTED_MEDIA,
        NDIS_STATUS_GROUP_ADDRESS_IN_USE,
        NDIS_STATUS_FILE_NOT_FOUND,
        NDIS_STATUS_ERROR_READING_FILE,
        NDIS_STATUS_ALREADY_MAPPED,
        NDIS_STATUS_RESOURCE_CONFLICT,
        NDIS_STATUS_TOKEN_RING_OPEN_ERROR,
        TP_STATUS_NO_SERVERS,
        TP_STATUS_NO_EVENTS
    };

#define StatusCount (sizeof(Status)/sizeof(NDIS_STATUS))

    static PUCHAR String[] = {
        "NDIS_STATUS_SUCCESS",
        "NDIS_STATUS_PENDING",
        "NDIS_STATUS_NOT_RECOGNIZED",
        "NDIS_STATUS_NOT_COPIED",
        "NDIS_STATUS_ONLINE",
        "NDIS_STATUS_RESET_START",
        "NDIS_STATUS_RESET_END",
        "NDIS_STATUS_RING_STATUS",
        "NDIS_STATUS_CLOSED",
        "NDIS_STATUS_WAN_LINE_UP",
        "NDIS_STATUS_WAN_LINE_DOWN",
        "NDIS_STATUS_WAN_FRAGMENT",
        "NDIS_STATUS_NOT_RESETTABLE",
        "NDIS_STATUS_SOFT_ERRORS",
        "NDIS_STATUS_HARD_ERRORS",
        "NDIS_STATUS_FAILURE",
        "NDIS_STATUS_RESOURCES",
        "NDIS_STATUS_CLOSING",
        "NDIS_STATUS_BAD_VERSION",
        "NDIS_STATUS_BAD_CHARACTERISTICS",
        "NDIS_STATUS_ADAPTER_NOT_FOUND",
        "NDIS_STATUS_OPEN_FAILED",
        "NDIS_STATUS_DEVICE_FAILED",
        "NDIS_STATUS_MULTICAST_FULL",
        "NDIS_STATUS_MULTICAST_EXISTS",
        "NDIS_STATUS_MULTICAST_NOT_FOUND",
        "NDIS_STATUS_REQUEST_ABORTED",
        "NDIS_STATUS_RESET_IN_PROGRESS",
        "NDIS_STATUS_CLOSING_INDICATING",
        "NDIS_STATUS_NOT_SUPPORTED",
        "NDIS_STATUS_INVALID_PACKET",
        "NDIS_STATUS_OPEN_LIST_FULL",
        "NDIS_STATUS_ADAPTER_NOT_READY",
        "NDIS_STATUS_ADAPTER_NOT_OPEN",
        "NDIS_STATUS_NOT_INDICATING",
        "NDIS_STATUS_INVALID_LENGTH",
        "NDIS_STATUS_INVALID_DATA",
        "NDIS_STATUS_BUFFER_TOO_SHORT",
        "NDIS_STATUS_INVALID_OID",
        "NDIS_STATUS_ADAPTER_REMOVED",
        "NDIS_STATUS_UNSUPPORTED_MEDIA",
        "NDIS_STATUS_GROUP_ADDRESS_IN_USE",
        "NDIS_STATUS_FILE_NOT_FOUND",
        "NDIS_STATUS_ERROR_READING_FILE",
        "NDIS_STATUS_ALREADY_MAPPED",
        "NDIS_STATUS_RESOURCE_CONFLICT",
        "NDIS_STATUS_TOKEN_RING_OPEN_ERROR",
        "TP_STATUS_NO_SERVERS",
        "TP_STATUS_NO_EVENTS"
    };

    static BYTE BadStatus[] = "UNDEFINED";
    DWORD i;

    for (i=0; i<StatusCount; i++) 
    {
        if (GeneralStatus == Status[i]) 
        {
            return String[i];
        }
    }
    return BadStatus;

#undef StatusCount
}



DWORD
TpctlGetCommandCode(
    LPSTR Argument
    )

{
    DWORD i;

    for ( i=1;i<NUM_COMMANDS;i++ ) 
    {
        if (_stricmp(  Argument, CommandCode[i].CmdAbbr ) == 0 ) 
        {
            return CommandCode[i].CmdCode;
        }

        if (_stricmp( Argument, CommandCode[i].CmdName ) == 0 ) 
        {
            return CommandCode[i].CmdCode;
        }
    }
    return CMD_ERR;
}



LPSTR
TpctlGetCommandName(
    LPSTR Command
    )

{
    DWORD i;

    for ( i=1;i<NUM_COMMANDS;i++ ) 
    {
        if (_stricmp(Command,CommandCode[i].CmdAbbr) == 0 ) 
        {
            return CommandCode[i].CmdName;
        }
        if (_stricmp(Command,CommandCode[i].CmdName) == 0 ) 
        {
            return CommandCode[i].CmdName;
        }
    }
    return CommandCode[CMD_ERR].CmdName;
}



LPSTR
TpctlGetCmdCode(
    DWORD CmdCode
    )
{
    static BYTE BadCmdCode[] = "UNDEFINED";

    DWORD i;

    for(i=1; i<NUM_COMMANDS; i++) 
    {
        if ( CmdCode == CommandCode[i].CmdCode ) 
        {
            return(CommandCode[i].CmdName);
        }
    }
    return BadCmdCode;
}



VOID
TpctlCopyAdapterAddress(
    DWORD OpenInstance,
    PREQUEST_RESULTS Results
    )
{
    DWORD i;
    PUCHAR Source, Destination;

    //
    // Sanjeevk: Bug# 5203: This routine needed modification to support
    //                      the additional NDIS_MEDIUM information sent
    //                      back
    //

    Source      =  (PUCHAR)( Results->InformationBuffer + sizeof( NDIS_MEDIUM ) );
    Destination =  (PUCHAR)( Open[OpenInstance].AdapterAddress );

    for (i=0;i<ADDRESS_LENGTH;i++) 
    {
        *Destination++ = *Source++;
    }
}



VOID
TpctlRecordArguments(
    IN TESTPARAMS Options[],
    IN DWORD      OptionTableSize,
    IN DWORD      argc,
    IN LPSTR      argv[TPCTL_MAX_ARGC]
    )

// -----------------
// 
// Routine Description:
// 
//     Create   Sanjeevk  7-1-93
// 
//     This function is responsible for creating the command in parts and records
//     it to the file accessed by ScriptRecordHandle
// 
// Arguments:
// 
//     Options          The TestParameter options from which the command is created
// 
//     OptionTableSize  The size of the table for the option under consideration
// 
//     argc             The number of arguments passed on the TPCTL command line
//                      prompt
// 
//     argv             The arguments passed on the TPCTL command line prompt
// 
// 
// Return Value:
// 
//     None
// 
// -------------------


{
    DWORD  i;
    CHAR   TmpBuffer[256];
    DWORD  BytesWritten,Status  ;
    DWORD  CmdCode = TpctlGetCommandCode( argv[0] );


    //
    // 1. Clear the temporary buffer which will be used to construct an option
    //    one at a time
    //
    ZeroMemory ( TmpBuffer, 256 );

    //
    // 2. Attempt to access the complete name of the command code.
    //
    if ( CmdCode == CMD_ERR ) 
    {
        sprintf( TmpBuffer, "%s", argv[0] );
    } 
    else 
    {
        sprintf( TmpBuffer, "%s", TpctlGetCommandName(argv[0]) );
    }

    //
    // 3. Write the first argument accessed into the script file
    //

    if ( !WriteFile(ScriptRecordHandle,
                    TmpBuffer,
                    strlen( TmpBuffer ),
                    &BytesWritten,
                    NULL )) 
    {
        Status = GetLastError();
        printf("\n\tTpctlRecordArguments: write to script record file failed, returned 0x%lx\n",
                Status);
        return;
    }

    //
    // 4. Set up the buffer for reuse
    //
    ZeroMemory ( TmpBuffer, 256 );

    //
    // 5. Now for the number of argument passed on the TPCTL command prompt, reconstruct
    //    each sub option one at a time
    //
    for( i = 1; i < argc; i++ ) 
    {
        //
        // 5.a Check if a valid Option Table has been provided and if so get the
        //     the lvalue and rvalue and combine them to form an expression
        //
    
        if ( Options != NULL ) 
        {
            sprintf( TmpBuffer, "\t+\n  %s=%s", Options[i-1].ArgName, argv[i] );
        } 
        else 
        {
            if ( CmdCode != CMD_ERR ) 
            {
                sprintf( TmpBuffer, "\t+\n  %s", argv[i] );
            } 
            else 
            {
                sprintf( TmpBuffer, " %s", argv[i] );
            }
        }

        //
        // 5.b Write this reconstructed string which now signifies the complete
        //     sub-option into the script file
        //
    
        if ( !WriteFile(ScriptRecordHandle,
                        TmpBuffer,
                        strlen( TmpBuffer ),
                        &BytesWritten,
                        NULL )) 
        {
            Status = GetLastError();
            printf("\n\tTpctlRecordArguments: write to script record file failed, returned 0x%lx\n",
                        Status);
            return;
        }

        //
        // 5.c And clear the buffer for reuse(next sub-option)
        //
        ZeroMemory ( TmpBuffer, 256 );

    }

    //
    // 6. Since it is possible to specifiy one or more suboptions and the command prompt
    //    we must dteremine all of the lvalues and rvalues of the current option
    //    Since we can also specify a semicolon to accept default values, we must
    //    carefully consider the various types of data associated with the rvalues
    //
    for( i = argc; i <= OptionTableSize; i++ ) 
    {
        PUCHAR p;

        switch ( Options[i-1].TestType ) 
        {
            case Integer :
                sprintf( TmpBuffer, "\t+\n  %s=%ld", Options[i-1].ArgName, 
                                                     *(PDWORD)Options[i-1].Destination );
                break;

            case String :
                sprintf(TmpBuffer, "\t+\n  %s=%s", Options[i-1].ArgName, Options[i-1].Destination);
                break;

            case Address4 :
                p = Options[i-1].Destination;
                sprintf( TmpBuffer, "\t+\n  %s=%02x-%02x-%02x-%02x", Options[i-1].ArgName,
                         *p, *(p+1), *(p+2), *(p+3) );
                break;

            case Address6 :
                p = Options[i-1].Destination;
                sprintf( TmpBuffer, "\t+\n  %s=%02x-%02x-%02x-%02x-%02x-%02x", Options[i-1].ArgName,
                         *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5) );
                break;

            case ParsedInteger :
                p = Options[i-1].Destination;
                sprintf( TmpBuffer, "\t+\n  %s=0x%4.4x", Options[i-1].ArgName, *(LPDWORD)p );
                break;
        }

        if ( !WriteFile(ScriptRecordHandle,
                        TmpBuffer,
                        strlen( TmpBuffer ),
                        &BytesWritten,
                        NULL )) 
        {
            Status = GetLastError();
            printf("\n\tTpctlRecordArguments: write to script record file failed, returned 0x%lx\n",                    Status);
            return;
        }

        ZeroMemory ( TmpBuffer, 256 );

    }

    //
    // 7. Finally add the newline to end the command
    //
    sprintf( TmpBuffer, "\n\n" );
    if ( !WriteFile(ScriptRecordHandle,
                    TmpBuffer,
                    strlen( TmpBuffer ),
                    &BytesWritten,
                    NULL )) 
    {
        Status = GetLastError();
        printf("\n\tTpctlRecordArguments: write to script record file failed, returned 0x%lx\n",
                Status);
    }

}


LPSTR
TpctlEnumerateRegistryInfo(
     IN PUCHAR TmpBuf,
     IN PUCHAR DbaseName,
     IN PUCHAR SubKeyName,
     IN PUCHAR ValueName,
     IN DWORD  ReadValueType,
     IN PUCHAR ReadValue,
     IN DWORD  ReadValueSize )
{

    INT     i;

    TmpBuf += sprintf( TmpBuf, 
                        "\tDataBase Name  = %s\n\tSub Key Name   = %s\n\tValue Name     = %s\n",
                        DbaseName, SubKeyName, ValueName );

    TmpBuf += sprintf( TmpBuf, "\tValue Type     = %s\n", TpctlGetValueType( ReadValueType ) );

    switch( ReadValueType ) 
    {
        case REG_BINARY :
            TmpBuf += sprintf( TmpBuf, "\tValue(IN HEX)  = ");
            for( i = 0; i < (INT)ReadValueSize; i++ ) 
            {
                if ( i%6 || (i == 0) ) 
                {
                    TmpBuf += sprintf( TmpBuf, "%2.2x ", ReadValue[i] );
                } 
                else 
                {
                    TmpBuf += sprintf( TmpBuf, "\n\t                 %2.2x ", ReadValue[i] );
                }
            }
            TmpBuf += sprintf( TmpBuf, "\n" );
            break;

        case REG_DWORD :
            TmpBuf += sprintf( TmpBuf, "\tValue          = 0x%lx\n", *(LPDWORD)ReadValue );
            break;

            //
            // This code section had to be commented out because the idiot who defined
            // the types made LITTLE_ENDIAN = DWORD. If we were to port over to a
            // BIG_ENDIAN system, we would have to comment out the code for BIG_ENDIAN
            //
            //  case REG_DWORD_LITTLE_ENDIAN :
            //      TmpBuf += sprintf( TmpBuf, "\tValue = LITTLE_ENDIAN 0x" );
            //      for( i = 0 ; i < ReadValueSize ; i++ ) 
            //      {
            //          TmpBuf += sprintf( TmpBuf, "%2.2x", ReadValue[i] );
            //      }
            //      TmpBuf += sprintf( TmpBuf, " DWORD VALUE 0x%lx\n",  *(LPDWORD)ReadValue );
            //      break;

        case REG_DWORD_BIG_ENDIAN:
            TmpBuf += sprintf( TmpBuf, "\tValue          = BIG_ENDIAN 0x" );
            for( i =  0 ; i < (INT)ReadValueSize ; i++ ) 
            {
                TmpBuf += sprintf( TmpBuf, "%2.2x", ReadValue[i] );
            }
            TmpBuf += sprintf( TmpBuf, " DWORD VALUE 0x" );
            for( i =  0 ; i < (INT)ReadValueSize ; i++ ) 
            {
                TmpBuf += sprintf( TmpBuf, "%2.2x", ReadValue[i] );
            }
            TmpBuf += sprintf( TmpBuf, "\n" );
            break;

        case REG_LINK:
        case REG_EXPAND_SZ:
            TmpBuf += sprintf( TmpBuf, "\tValue          = %s\n", ReadValue );
            break;

        case REG_MULTI_SZ:
            TmpBuf += sprintf( TmpBuf, "\tValue(s)\n" );
            {
                PUCHAR Tmp1 = ReadValue;

                while ( strlen( Tmp1 ) != 0 ) 
                {
                    TmpBuf += sprintf( TmpBuf, "\t\t%s\n", Tmp1 );
                    Tmp1   += (strlen( Tmp1 ) + 1);
                }
            }
            break;

        case REG_NONE:
            TmpBuf += sprintf( TmpBuf, "\tValue          = %s\n", ReadValue );
            break;

        case REG_RESOURCE_LIST:
            TmpBuf += sprintf( TmpBuf, "\tValue          = %s\n", ReadValue );
            break;

        case REG_SZ:
            TmpBuf += sprintf( TmpBuf, "\tValue          = %s\n", ReadValue );
            break;

        default:
            TmpBuf += sprintf( TmpBuf, "\tValue          = UNKNOWN\n" );
            break;

    }

    return TmpBuf;

}


LPSTR
TpctlGetValueType(
    IN DWORD ValueType
                 )
{
    static UCHAR ValueTypeString[20];

    ZeroMemory( ValueTypeString, 20 );

    switch ( ValueType ) 
    {
        case REG_BINARY :
            strcpy( ValueTypeString, "REG_BINARY" );
            break;

        case REG_DWORD :
            strcpy( ValueTypeString, "REG_DWORD" );
            break;
            //
            // This code section had to be commented out because the idiot who defined
            // the types made LITTLE_ENDIAN = DWORD. If we were to port over to a
            // BIG_ENDIAN system, we would have to comment out the code for BIG_ENDIAN
            //
            //  case REG_DWORD_LITTLE_ENDIAN :
            //      strcpy( ValueTypeString, "REG_DWORD_LITTLE_ENDIAN" );
            //      break;
            //

        case REG_DWORD_BIG_ENDIAN :
            strcpy( ValueTypeString, "REG_DWORD_BIG_ENDIAN" );
            break;

        case REG_EXPAND_SZ :
            strcpy( ValueTypeString, "REG_EXPAND_SZ" );
            break;

        case REG_LINK :
            strcpy( ValueTypeString, "REG_LINK" );
            break;

        case REG_MULTI_SZ :
            strcpy( ValueTypeString, "REG_MULTI_SZ" );
            break;

        case REG_NONE :
            strcpy( ValueTypeString, "REG_NONE" );
            break;

        case REG_RESOURCE_LIST :
            strcpy( ValueTypeString, "REG_RESOURCE_LIST" );
            break;

        case REG_SZ :
            strcpy( ValueTypeString, "REG_SZ" );
            break;

        default :
            strcpy( ValueTypeString, "UNDEFINED" );
            break;
    }

    return ValueTypeString;

}