/* File:  main.c */

#define MAIN

#ifdef _WIN32
#include <windows.h>
#include <io.h>
#include <process.h>
#endif /* _WIN32 */

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

#include "ar.h"
#include "api.h"
#include "get.h"
#include "globals.h"
#include "main.h"
#include "util.h"
#include "print.h"

#ifdef AR_VERS_H_FILE
#include "arvers.h"
#else /* AR_VERS_H_FILE */
#define AR_VERSION_DRIVER "5.00.00"
#endif /* AR_VERS_H_FILE */

void LaunchThread(ARBoolean waitFlag);
void LaunchRandomNumberThread();

#ifndef OUTPUT_MODE
void InitCommandProcessing()
{
   /* perform any generic initialization operations here */
   LaunchRandomNumberThread();
}

void TermCommandProcessing()
{
   /* perform any generic cleanup operations here */
}
#else
void InitCommandProcessing();
void TermCommandProcessing();
#endif

#ifdef _WIN32
/*****************************************************************************/
/*                                                                           */
/*                             SetConsoleSize                                */
/*                                                                           */
/*****************************************************************************/

void SetConsoleSize()
{
   STARTUPINFO startInfo;      /* attributes with which we were started */
   COORD coordinates;          /* screen buffer coordinates */
   SMALL_RECT rect;            /* window attributes */
   DWORD err;                  /* error return from window routines */

   GetStartupInfo(&startInfo); /* get the handle to our console */
   if ((startInfo.dwFlags & STARTF_USESTDHANDLES) == 0)
   {                           /* if we don't use handles from startInfo */
                               /* get what we do use */
      startInfo.hStdOutput = (HANDLE) _get_osfhandle(fileno(stdout));
   }

   coordinates.X = 81;         /* set the new screen buffer size */
   coordinates.Y = 999;
   if (SetConsoleScreenBufferSize(startInfo.hStdOutput, coordinates) != TRUE)
      err = GetLastError();

   rect.Left = 0;              /* set the new window size */
   rect.Top = 0;
   rect.Right = 80;
   rect.Bottom = 48;
   if (SetConsoleWindowInfo(startInfo.hStdOutput, TRUE, &rect) != TRUE)
      err = GetLastError(); 
}
#endif /* _WIN32 */


/*****************************************************************************/
/*                                                                           */
/*                         EstablishThreadEnvironment                        */
/*                                                                           */
/*****************************************************************************/

ARBoolean EstablishThreadEnvironment()
{
   DriverTLSAlloc(&gTLSKey);

   DriverInitializeCriticalSection(&gRandomNumberCriticalSection);

   DriverCreateEvent(&gRandomNumberRequestEvent, RELEASE_ONE);
   DriverCreateEvent(&gRandomNumberReplyEvent, RELEASE_ONE);

   return TRUE;
}


/*****************************************************************************/
/*                                                                           */
/*                          CleanupThreadEnvironment                         */
/*                                                                           */
/*****************************************************************************/

void CleanupThreadEnvironment()
{
   DriverTLSFree(gTLSKey);

   DriverDeleteCriticalSection(&gRandomNumberCriticalSection);

   DriverDeleteEvent(&gRandomNumberRequestEvent);
   DriverDeleteEvent(&gRandomNumberReplyEvent);
}


/*****************************************************************************/
/*                                                                           */
/*                             ProcessCommandLine                            */
/*                                                                           */
/*****************************************************************************/

ARBoolean ProcessCommandLine(argc, argv)
int     argc;        /* IN; number of items in command line */
char   *argv[];      /* IN; array of command line arguments */

{
   int                 i;                     /* working index */
   unsigned int        maxArgumentLen;        /* argument length maximum */
   char                option;                /* option specified */
   char               *tempPtr;               /* working pointer */
   char               *cp;                    /* temp pointer into buffer */
   ThreadControlBlock *threadControlBlockPtr; /* control block pointer */

   threadControlBlockPtr = (ThreadControlBlock *) GetThreadControlBlockPtr();

   for (i = 1; i < argc; i++)
   {                              /* process each entry on the command line */
      if ((argv[i][0] == '-') &&
          (argv[i][1] == 'u' || argv[i][1] == 'p' || argv[i][1] == 'l' ||
           argv[i][1] == 's' || argv[i][1] == 'x' || argv[i][1] == 'o' ||
           argv[i][1] == 'd' || argv[i][1] == 'q' || argv[i][1] == 'g' ||
           argv[i][1] == 'c' || argv[i][1] == 'a'))
         option = argv[i][1];
      
      else
      {                           /* unrecognized option */
         DriverPrintError("Unrecognized option %s\n", argv[i]);
         return FALSE;
      }
                                  /* have option, load the value */
      if (strlen(argv[i]) > (size_t) 2)
         tempPtr = &(argv[i][2]);
      else
      {                           /* check next argument for name */
         i++;
         if (i < argc)
            tempPtr = argv[i];
         else
         {                        /* no next argument so error */
            DriverPrintError("Missing value for -%c option\n", option);
            return FALSE;
         }
      }
                                  /* get max argument length */
      switch (option)
      {
         case 'a' :
         case 'u' :
         case 'p' :
         case 'l' :
         case 's' :
            maxArgumentLen = AR_MAX_NAME_SIZE;
            break;
         case 'x' :
         case 'd' :
            maxArgumentLen = AR_MAX_FULL_FILENAME;
            break;
         case 'o' :
         case 'q' :
            maxArgumentLen = 3;
            break;
         case 'g' :
            maxArgumentLen = 10;
            break;
         case 'c' :
            maxArgumentLen = 5;
            break;
      }

      if (strlen(tempPtr) > maxArgumentLen)
      {                           /* argument too long so error */
         DriverPrintError("Value for -%c option is too long: %s\n", option,
                          tempPtr);
         return FALSE;
      }
                                  /* take appropriate action */
      switch (option)
      {
         case 'u' :
            cp = strchr(tempPtr, '\\');
            if (cp)
            {
               *cp = '\0';
               cp++;
               (void) strcpy(threadControlBlockPtr->control.authString, tempPtr);
            }
            else
            {
               threadControlBlockPtr->control.authString[0] = '\0';
               cp = tempPtr;
            }
            (void) strcpy(threadControlBlockPtr->control.user, cp);
            break;
         case 'a' :
            (void) strcpy(threadControlBlockPtr->control.authString, tempPtr);
            break;
         case 'p' :
            (void) strcpy(threadControlBlockPtr->control.password, tempPtr);
            break;
         case 'l' :
            (void) strcpy(threadControlBlockPtr->control.language, tempPtr);
            break;
         case 's' :
            (void) strcpy(threadControlBlockPtr->control.server, tempPtr);
            break;
         case 'x' :
            (void) OpenInputFile(tempPtr);
            break;
         case 'o' :
            gOutputSetting = (unsigned long) atol(tempPtr);
            break;
         case 'd' :
            (void) strcpy(gResultDir, tempPtr);
            break;
         case 'q' :
            gQuietMode = (unsigned long) atol(tempPtr);
            break;
         case 'g' :
            gRandomNumberSeed = (unsigned int) atol(tempPtr);
            break;
         case 'c' :
            gOutputCount = (unsigned long) atol(tempPtr);
            break;
      }
   }

   return TRUE;
}


/*****************************************************************************/
/*                                                                           */
/*                                PrintMainMenu                              */
/*                                                                           */
/*****************************************************************************/

void PrintMainMenu()

{
                                     /* print main command menu */
   /* columns       0xxxxxxxx1xxxxxxxxx2xxxxxxxxx3xxxxxxxxx4xxxxxxxxx5xxxxxxxxx6xxxxxxxxx7xxxxxxxxx8 */
   /*               1        0         0         0         0         0         0         0         0 */
   DriverPrintMenu("\n");
   DriverPrintMenu(" Active Link   Escalation      Filter        Entry           Entry\n");
   DriverPrintMenu(" -----------   ----------      ------        -----           -----\n");
   DriverPrintMenu(" get    (gal)  get    (ges)    get    (gf)   get       (ge)  merge    (me)\n");
   DriverPrintMenu(" set    (sal)  set    (ses)    set    (sf)   set       (se)  stats    (stat)\n");
   DriverPrintMenu(" create (cal)  create (ces)    create (cf)   create    (ce)  get BLOB (geb)\n");
   DriverPrintMenu(" delete (dal)  delete (des)    delete (df)   delete    (de)  getmult  (gme)\n");
   DriverPrintMenu(" getlist(glal) getlist(gles)   getlist(glf)  getlist   (gle)\n");
   DriverPrintMenu(" getmult(gmal)                               getlistw/f(glewf)\n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" Container     Schema          Char Menu     Schema Field    VUI\n");
   DriverPrintMenu(" ---------     ------          ---------     ------------    ---\n");
   DriverPrintMenu(" get    (gco)  get    (gs)     get    (gc)   get     (gsf)   get    (gv)\n");
   DriverPrintMenu(" set    (sco)  set    (ss)     set    (sc)   set     (ssf)   set    (sv)\n");
   DriverPrintMenu(" create (cco)  create (cs)     create (cc)   create  (csf)   create (cv)\n");
   DriverPrintMenu(" delete (dco)  delete (ds)     delete (dc)   delete  (dsf)   delete (dv)\n");
   DriverPrintMenu(" getlist(glco) getlist(gls)    getlist(glc)  getlist (glsf)  getlist(glsv)\n");
   DriverPrintMenu("               gls w/a(glsa)   expand (ec)   getmult (gmsf)\n");
   DriverPrintMenu("               getmult(gms)    exp ssm(essm) delmult (dmsf)\n");
   DriverPrintMenu("               getlist ext(glxsc)            getmult ext cands(gmxfc)\n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" License       Alert           Info          Encode/Decode   Thread\n");
   DriverPrintMenu(" -------       -----           ----          -------------   ------\n");
   DriverPrintMenu(" val    (vl)   create   (cae)  get svr (gsi) enquery(ecqal)  launch      (lt)\n");
   DriverPrintMenu(" valmult(vml)  register (rfa)  set svr (ssi) dequery(dcqal)  launch wait (lwt)\n");
   DriverPrintMenu(" getlist(gll)  deregistr(dfa)  get FT  (gft) enassig(ecasn)  release wait(rwt)\n");
   DriverPrintMenu(" create (cl)   gla user (glau) set FT  (sft) deassig(dcasn)\n");
   DriverPrintMenu(" delete (dl)   get count(gac)  get stat(gss) echstry(echst)\n");
   DriverPrintMenu("               decode   (dcam)               ecdiary(ecdia)\n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" Init/Term     Control/Logging Misc Lists    Misc            Misc\n");
   DriverPrintMenu(" ---------     --------------- ----------    ----            ----\n");
   DriverPrintMenu(" init  (init)  record   (rec)  server(svr)   ver user (ver)  get file   (gfl)\n");
   DriverPrintMenu(" term  (term)  stop rec (srec) group (glg)   export   (exp)  set file   (sfl)\n");
   DriverPrintMenu(" help  (h, ?)  open out (oout) user  (glu)   import   (imp)  get errmsg (gem)\n");
   DriverPrintMenu(" exit  (e, q)  close out(cout) sql   (glsql) exec proc(proc) set logging(slog)\n");
   DriverPrintMenu(" login (log)   execute  (ex)   sql al(sqlal) exec p al(epal) close conn (cnc)\n");
   DriverPrintMenu("               bgn loop (bl)                 load qual(lqs)  valid cache(vfc)\n");
   DriverPrintMenu("               end loop (el)                 set port (ssp)  driver vers(dver)\n");
   DriverPrintMenu("                                             signal   (sig)\n");
   DriverPrintMenu(" Localized Val Session Config  Sleep Timer \n");
   DriverPrintMenu(" ------------- --------------  -----------\n");
   DriverPrintMenu(" get    (glv)  get conf(gsc)   sleep  (st) \n");
   DriverPrintMenu(" getmult(gmlv) set conf(ssc)   random (rst)\n");
   DriverPrintMenu("                               msec   (msst)\n");
}


/*****************************************************************************/
/*                                                                           */
/*                                GetNextCommand                             */
/*                                                                           */
/*****************************************************************************/

int GetNextCommand(
char **args                 /* pointer to arguments */
)

{
   int  commandCode;        /* code for the command requested */
   int  i;                  /* working index */
   ThreadControlBlock *threadControlBlockPtr; /* control block pointer */

   threadControlBlockPtr = (ThreadControlBlock *) GetThreadControlBlockPtr();
   threadControlBlockPtr->args[0] = '\0';
   *args = NULL;

   DriverPrintPrompt("\nCommand: ");

   commandCode = -99;
   while (commandCode == -99)
   {                               /* read and validate the command */
      char *blank;

      GetInputLine();
      blank = strchr(threadControlBlockPtr->buffer, ' ');
      if (blank)
      {
         *blank++ = '\0';
         strncpy(threadControlBlockPtr->args, blank, ARG_BUFFER_LEN - 1);
         threadControlBlockPtr->args[ARG_BUFFER_LEN - 1] = '\0';
         *args = threadControlBlockPtr->args;
      }
      if ((strlen(threadControlBlockPtr->buffer) == 0) ||
          (threadControlBlockPtr->buffer[0] == '#'))
         DriverPrintPrompt("\nCommand: "); /* blank line or comment */
      else if (strlen(threadControlBlockPtr->buffer) > (size_t) 5)
      {
         DriverPrintError(" *** Command too long, unrecognized ***\n");
         DriverPrintPrompt("\nCommand: ");
      }
      else if ((strcmp(threadControlBlockPtr->buffer, "h") == 0) ||
               (strcmp(threadControlBlockPtr->buffer, "?") == 0))
      {
         PrintMainMenu();
         DriverPrintPrompt("\nCommand: ");
      }
      else if ((strcmp(threadControlBlockPtr->buffer, "e") == 0) ||
               (strcmp(threadControlBlockPtr->buffer, "q") == 0) ||
               (strcmp(threadControlBlockPtr->buffer, "x") == 0))
         commandCode = COMMAND_EXIT;
      else
      {
         for (i = 0; i <= MAX_COMMAND; i++)
            if (strcmp(threadControlBlockPtr->buffer, commands[i]) == 0)
               break;             /* found a match so have a command */
         if (i <= MAX_COMMAND)
         {
            commandCode = i;
            strcpy(threadControlBlockPtr->currCommand,
                   threadControlBlockPtr->buffer);
         }
         else
         {
            DriverPrintError(" *** Command not recognized ***\n");
            DriverPrintPrompt("\nCommand: ");
         }
      }
   }

   return commandCode;
}


void PrintDriverVersion(void)
{
   char buffer[256];

   sprintf(buffer, "%s\n", AR_VERSION_DRIVER);
   DriverPrintResult(buffer);
}

/*****************************************************************************/
/*                                                                           */
/*                               ProcessCommands                             */
/*                                                                           */
/*****************************************************************************/

void ProcessCommands()

{
   int  commandCode;        /* code for the command requested */
   char *args;              /* pointer to arguments */

   /* process commands until exit specified */

   while ((commandCode = GetNextCommand(&args)) != COMMAND_EXIT)
   {                              
      switch (commandCode)
      {
         case COMMAND_LOGIN             :
            GetARControlStruct();
            break;
         case COMMAND_GET_ENTRY         :
            APIARGetEntry();
            break;
         case COMMAND_SET_ENTRY         :
            APIARSetEntry();
            break;
         case COMMAND_CREATE_ENTRY      :
            APIARCreateEntry();
            break;
         case COMMAND_DELETE_ENTRY      :
            APIARDeleteEntry();
            break;
         case COMMAND_GETLIST_ENTRY     :
            APIARGetListEntry();
            break;
         case COMMAND_GETLIST_ENTRY_WITH_FIELDS     :
            APIARGetListEntryWithFields();
            break;
         case COMMAND_GET_FILTER        :
            APIARGetFilter();
            break;
         case COMMAND_SET_FILTER        :
            APIARSetFilter();
            break;
         case COMMAND_CREATE_FILTER     :
            APIARCreateFilter();
            break;
         case COMMAND_DELETE_FILTER     :
            APIARDeleteFilter();
            break;
         case COMMAND_GETLIST_FILTER    :
            APIARGetListFilter();
            break;
         case COMMAND_GET_ESCALATION        :
            APIARGetEscalation();
            break;
         case COMMAND_SET_ESCALATION        :
            APIARSetEscalation();
            break;
         case COMMAND_CREATE_ESCALATION     :
            APIARCreateEscalation();
            break;
         case COMMAND_DELETE_ESCALATION     :
            APIARDeleteEscalation();
            break;
         case COMMAND_GETLIST_ESCALATION    :
            APIARGetListEscalation();
            break;
         case COMMAND_GETLIST_GROUP     :
            APIARGetListGroup();
            break;
         case COMMAND_GET_SCHEMA        :
            APIARGetSchema();
            break;
         case COMMAND_SET_SCHEMA        :
            APIARSetSchema();
            break;
         case COMMAND_CREATE_SCHEMA     :
            APIARCreateSchema();
            break;
         case COMMAND_DELETE_SCHEMA     :
            APIARDeleteSchema();
            break;
         case COMMAND_GETLIST_SCHEMA    :
            APIARGetListSchema();
            break;
         case COMMAND_GETMULT_SCHEMA    :
            APIARGetMultipleSchemas();
            break;
         case COMMAND_GET_SCH_FIELD     :
            APIARGetField();
            break;
         case COMMAND_SET_SCH_FIELD     :
            APIARSetField();
            break;
         case COMMAND_CREATE_SCH_FIELD  :
            APIARCreateField();
            break;
         case COMMAND_DELETE_SCH_FIELD  :
            APIARDeleteField();
            break;
         case COMMAND_GETLIST_SCH_FIELD :
            APIARGetListField();
            break;
         case COMMAND_GET_CHAR_MENU     :
            APIARGetCharMenu();
            break;
         case COMMAND_SET_CHAR_MENU     :
            APIARSetCharMenu();
            break;
         case COMMAND_CREATE_CHAR_MENU  :
            APIARCreateCharMenu();
            break;
         case COMMAND_DELETE_CHAR_MENU  :
            APIARDeleteCharMenu();
            break;
         case COMMAND_GETLIST_CHAR_MENU : 
            APIARGetListCharMenu();
            break;
         case COMMAND_GET_VUI           :
            APIARGetVUI();
            break;
         case COMMAND_SET_VUI           :
            APIARSetVUI();
            break;
         case COMMAND_CREATE_VUI        :
            APIARCreateVUI();
            break;
         case COMMAND_DELETE_VUI        :
            APIARDeleteVUI();
            break;
         case COMMAND_GETLIST_VUI       :
            APIARGetListVUI();
            break;
         case COMMAND_EXPORT            :
            APIARExport();
            break;
         case COMMAND_IMPORT            :
            APIARImport();
            break;
         case COMMAND_GET_SERVER_INFO   :
            APIARGetServerInfo();
            break;
         case COMMAND_VERIFY_USER       :
            APIARVerifyUser();
            break;
         case COMMAND_EXECUTE           :
            OpenInputFile(args);
            break;
         case COMMAND_OPEN_OUT          :
            OpenOutputFile();
            break;
         case COMMAND_CLOSE_OUT         :
            CloseOutputFile();
            break;
         case COMMAND_RECORD            :
            StartRecording();
            break;
         case COMMAND_STOP_RECORD       :
            StopRecording();
            break;
         case COMMAND_LAUNCH_THREAD :
            LaunchThread(FALSE);
            break;
         case COMMAND_LAUNCH_WAITING_THREAD :
            LaunchThread(TRUE);
            break;
         case COMMAND_RELEASE_WAITING_THREADS :
            ReleaseWaitingThreads();
            break;
         case COMMAND_SLEEP_TIMER       :
            SleepTimer();
            break;
         case COMMAND_RANDOM_SLEEP_TIMER:
            RandomSleepTimer();
            break;
         case COMMAND_MILLISECOND_SLEEP_TIMER:
            MillisecondSleepTimer();
            break;
         case COMMAND_BEGIN_LOOP:
            BeginLoop();
            break;
         case COMMAND_END_LOOP:
            EndLoop();
            break;
         case COMMAND_GETLIST_SERVER    :
            APIARGetListServer();
            break;
         case COMMAND_INITIALIZATION    :
            APIARInitialization();
            break;
         case COMMAND_TERMINATION       :
            APIARTermination();
            break;
         case COMMAND_GET_ACTIVE_LINK   :
            APIARGetActiveLink();
            break;
         case COMMAND_SET_ACTIVE_LINK   :
            APIARSetActiveLink();
            break;
         case COMMAND_CREATE_ACTIVE_LINK:
            APIARCreateActiveLink();
            break;
         case COMMAND_DELETE_ACTIVE_LINK:
            APIARDeleteActiveLink();
            break;
         case COMMAND_GETLIST_ACTIVE_LINK:
            APIARGetListActiveLink();
            break;
         case COMMAND_GET_MULTIPLE_ACTIVE_LINKS:
            APIARGetMultipleActiveLinks();
            break;
         case COMMAND_MERGE_ENTRY       :
            APIARMergeEntry();
            break;
         case COMMAND_LOAD_AR_QUAL_STRUCT:
            APIARLoadARQualifierStruct();
            break;
         case COMMAND_EXPAND_CHAR_MENU   :
            APIARExpandCharMenu();
            break;
         case COMMAND_SET_SERVER_INFO    :
            APIARSetServerInfo();
            break;
         case COMMAND_GETLIST_USER      :
            APIARGetListUser();
            break;
         case COMMAND_ENTRY_STATISTICS  :
            APIARGetEntryStatistics();
            break;
         case COMMAND_SETFULLTEXT_INFO   :
            APIARSetFullTextInfo ();
            break;
         case COMMAND_GETFULLTEXT_INFO   :
            APIARGetFullTextInfo ();
            break;
         case COMMAND_GET_SERVER_STAT   :
            APIARGetServerStatistics();
            break;
         case COMMAND_GETLIST_SQL       :
            APIARGetListSQL();
            break;
         case COMMAND_DELETE_MULTI_FIELD:
            APIARDeleteMultipleFields();
            break;
         case COMMAND_EXECUTE_PROCESS   :
            APIARExecuteProcess();
            break;
         case COMMAND_SET_SERVER_PORT   :
            APIARSetServerPort();
            break;
         case COMMAND_GET_MULTIPLE_ENTRY :
            APIARGetMultipleEntries();
            break;
         case COMMAND_GET_SUPPORT_FILE :
            APIARGetSupportFile();
            break;
         case COMMAND_SET_SUPPORT_FILE :
            APIARSetSupportFile();
            break;
         case COMMAND_CREATE_SUPPORT_FILE :
            APIARCreateSupportFile();
            break;
         case COMMAND_DELETE_SUPPORT_FILE :
            APIARDeleteSupportFile();
            break;
         case COMMAND_GETLIST_SUPPORT_FILE :
            APIARGetListSupportFile();
            break;
         case COMMAND_GETENTRY_BLOB :
            APIARGetEntryBLOB();
            break;
         case COMMAND_GET_CONTAINER :
            APIARGetContainer();
            break;
         case COMMAND_SET_CONTAINER :
            APIARSetContainer();
            break;
         case COMMAND_CREATE_CONTAINER :
            APIARCreateContainer();
            break;
         case COMMAND_DELETE_CONTAINER :
            APIARDeleteContainer();
            break;
         case COMMAND_GETLIST_CONTAINER :
            APIARGetListContainer();
            break;
         case COMMAND_GET_ERROR_MESSAGE :
            APIARGetTextForErrorMessage();
            break;
         case COMMAND_SET_LOGGING:
            APIARSetLogging();
            break;
         case COMMAND_CLOSE_NET_CONNECTIONS:
            APIARCloseNetworkConnections();
            break;
         case COMMAND_SIGNAL:
            APIARSignal();
            break;
         case COMMAND_VALIDATE_FORM_CACHE:
            APIARValidateFormCache();
            break;
         case COMMAND_GET_MULTIPLE_FIELDS:
            APIARGetMultipleFields();
            break;
         case COMMAND_GET_LOCALIZED_VALUE:
            APIARGetLocalizedValue();
            break;
         case COMMAND_GET_MULT_LOCALIZED_VALUES:
            APIARGetMultipleLocalizedValues();
            break;
         case COMMAND_GETLIST_SCHEMA_WITH_ALIAS:
            APIARGetListSchemaWithAlias();
            break;
         case COMMAND_CREATE_ALERT_EVENT:
            APIARCreateAlertEvent();
            break;
         case COMMAND_REGISTER_ALERTS:
            APIARRegisterForAlerts();
            break;
         case COMMAND_DEREGISTER_ALERTS:
            APIARDeregisterForAlerts();
            break;
         case COMMAND_GETLIST_ALERT_USER:
            APIARGetListAlertUser();
            break;
         case COMMAND_GET_ALERT_COUNT:
            APIARGetAlertCount();
            break;
         case COMMAND_DECODE_ALERT_MESSAGE:
            APIARDecodeAlertMessage();
            break;
         case COMMAND_ENCODE_QUALIFIER:
            APIAREncodeARQualifierStruct();
            break;
         case COMMAND_DECODE_QUALIFIER:
            APIARDecodeARQualifierStruct();
            break;
         case COMMAND_ENCODE_ASSIGN:
            APIAREncodeARAssignStruct();
            break;
         case COMMAND_DECODE_ASSIGN:
            APIARDecodeARAssignStruct();
            break;
         case COMMAND_ENCODE_HISTORY:
            APIAREncodeStatusHistory();
            break;
         case COMMAND_ENCODE_DIARY:
            APIAREncodeDiary();
            break;
         case COMMAND_GETLIST_EXT_SCHEMA_CANDS :
            APIARGetListExtSchemaCandidates();
            break;
         case COMMAND_GET_MULT_EXT_FIELD_CANDS :
            APIARGetMultipleExtFieldCandidates();
            break;
         case COMMAND_EXPAND_SS_MENU:
            APIARExpandSSMenu();
            break;
         case COMMAND_VALIDATE_LICENSE:
            APIARValidateLicense();
            break;
         case COMMAND_VALIDATE_MULTIPLE_LICENSES:
            APIARValidateMultipleLicenses();
            break;
         case COMMAND_GETLIST_LICENSE:
            APIARGetListLicense();
            break;
         case COMMAND_CREATE_LICENSE:
            APIARCreateLicense();
            break;
         case COMMAND_DELETE_LICENSE:
            APIARDeleteLicense();
            break;
         case COMMAND_GETLIST_SQL_FOR_AL:
            APIARGetListSQLForActiveLink();
            break;
         case COMMAND_EXECUTE_PROCESS_FOR_AL:
            APIARExecuteProcessForActiveLink();
            break;
         case COMMAND_DRIVER_VERSION:
            PrintDriverVersion();
            break;
         case COMMAND_GET_SESSION_CONFIGURATION:
            APIARGetSessionConfiguration();
            break;
         case COMMAND_SET_SESSION_CONFIGURATION:
            APIARSetSessionConfiguration();
            break;
         default                        :
            DriverPrintError(" **** No support for this command (%d) in driver \n",
                             commandCode);
            break;
      }
   }
}


/*****************************************************************************/
/*                                                                           */
/*                     RandomNumberThreadStartFunction                       */
/*                                                                           */
/*****************************************************************************/

#ifdef _WIN32
void __stdcall RandomNumberThreadStartFunction(void * dummyArg)
#else
void *RandomNumberThreadStartFunction(void * dummyArg)
#endif /* _WIN32 */

{
   unsigned int  randomNumberValue;

   /* seed the random number generator from within this thread */
   /* because some platforms have thread-safe implementations  */

   (void) srand(gRandomNumberSeed);

   /* gain control of the number request event so that this thread */
   /* is always waiting for a request before another thread is     */
   /* allowed to make a request                                    */

   DriverLockEvent(&gRandomNumberRequestEvent);

   /* continually process requests for random numbers */

   while (1)
   {
      /* get a random number value to have it ready when asked for */

      randomNumberValue = (unsigned int) rand();

      /* wait until we receive a number request event */

      DriverWaitForEvent(&gRandomNumberRequestEvent);

      /* transfer the random number we're holding to global data */

      gRandomNumberValue = randomNumberValue;

      /* signal the requesting thread that the number is ready */

      DriverSetEvent(&gRandomNumberReplyEvent);
   }

   /* if the above loop ever exited before program termination we would */
   /* unlock the request event here                                     */
}


/*****************************************************************************/
/*                                                                           */
/*                         LaunchRandomNumberThread                          */
/*                                                                           */
/*****************************************************************************/

void LaunchRandomNumberThread()

{
#ifdef _WIN32
   HANDLE              threadHandle;
   unsigned            threadId;
#else
   pthread_attr_t     *nullAttr = NULL;
   pthread_t           threadHandle;
#endif /* !_WIN32 */

   /* start the new thread that will be active for the life of the process */

#ifdef _WIN32
   if ((threadHandle = (HANDLE) _beginthreadex(NULL, 0,
                                               (unsigned int 
                                                (__stdcall *)(void *))
                                               RandomNumberThreadStartFunction,
                                               NULL, 0, &threadId)) == 0)
#else
   if (pthread_create(&threadHandle, nullAttr, RandomNumberThreadStartFunction,
                      NULL) != 0)
#endif /* _WIN32 */
   {
      DriverPrintError(" **** unable to start random number thread\n");
   }
   else
   {
      /* disconnect the handle since we don't monitor this thread */

#ifdef _WIN32
      CloseHandle(threadHandle);
#else
      pthread_detach(threadHandle);
#endif /* _WIN32 */
   }
}


/*****************************************************************************/
/*                                                                           */
/*                           ThreadStartFunction                             */
/*                                                                           */
/*****************************************************************************/

#ifdef _WIN32
void __stdcall ThreadStartFunction(void * threadStartInfoArg)
#else
void *ThreadStartFunction(void * threadStartInfoArg)
#endif /* _WIN32 */

{
   ThreadControlBlock * threadControlBlockPtr; /* control block pointer */
   ThreadStartInfo    * threadStartInfoPtr;    /* start info pointer */

   threadStartInfoPtr = (ThreadStartInfo *) threadStartInfoArg;

   /* create a control block for this newly created thread */

   if ((threadControlBlockPtr = CreateThreadControlBlock()) == NULL)
      return;

   /* transfer the input and output file pointers to the control block */

   threadControlBlockPtr->inFile[threadControlBlockPtr->currentInputDepth] =
      threadStartInfoPtr->inFile;
   threadControlBlockPtr->outFile = threadStartInfoPtr->outFile;

   /* transfer the output file name to the control block */

   if (threadStartInfoPtr->outFileName)
   {
      threadControlBlockPtr->outFileName =
         (char *) malloc(strlen(threadStartInfoPtr->outFileName) + 1);

      if (threadControlBlockPtr->outFileName)
         strcpy(threadControlBlockPtr->outFileName,
                threadStartInfoPtr->outFileName);
   }
   
   /* transfer the login info to the api control structure in the control */
   /* block                                                               */

   if (threadStartInfoPtr->userName)
      strcpy(threadControlBlockPtr->control.user,
             threadStartInfoPtr->userName);
   if (threadStartInfoPtr->authString)
      strcpy(threadControlBlockPtr->control.authString,
             threadStartInfoPtr->authString);
   if (threadStartInfoPtr->password)
      strcpy(threadControlBlockPtr->control.password,
             threadStartInfoPtr->password);
   if (threadStartInfoPtr->language)
      strcpy(threadControlBlockPtr->control.language,
             threadStartInfoPtr->language);
   if (threadStartInfoPtr->server)
      strcpy(threadControlBlockPtr->control.server,
             threadStartInfoPtr->server);

   /* free the memory used to facilitate the transfer of start information */

   if (threadStartInfoPtr->outFileName)
      free(threadStartInfoPtr->outFileName);
   if (threadStartInfoPtr->userName)
      free(threadStartInfoPtr->userName);
   if (threadStartInfoPtr->authString)
      free(threadStartInfoPtr->authString);
   if (threadStartInfoPtr->password)
      free(threadStartInfoPtr->password);
   if (threadStartInfoPtr->language)
      free(threadStartInfoPtr->language);
   if (threadStartInfoPtr->server)
      free(threadStartInfoPtr->server);

   /* if appropriate notify the parent thread that we are ready to start */
   /* processing commands and then wait until we are released to do so   */

   if (threadStartInfoPtr->waitFlag)
   {
      DriverLockEvent(threadStartInfoPtr->releaseWaitEventPtr);

      DriverSetEvent(threadStartInfoPtr->launchWaitEventPtr);

      DriverWaitForEvent(threadStartInfoPtr->releaseWaitEventPtr);

      DriverUnlockEvent(threadStartInfoPtr->releaseWaitEventPtr);
   }

   /* if requested we delay the processing of commands by a random length */
   /* sleep that is between zero and the provided number of seconds       */

   if (threadStartInfoPtr->upperBound > 0)
   {
      strcpy(threadControlBlockPtr->currCommand, "rst");
      RandomSleep(0, threadStartInfoPtr->upperBound);
   }

   /* free the thread start information structure */

   free((char *) threadStartInfoPtr);

   /* process the commands contained in the input file */

   ProcessCommands();

   /* remove the control block for this thread that is about to terminate */

   DestroyThreadControlBlock();
}


/*****************************************************************************/
/*                                                                           */
/*                               LaunchThread                                */
/*                                                                           */
/*****************************************************************************/

void LaunchThread(ARBoolean waitFlag)

{
   char                filename[AR_MAX_FULL_FILENAME + 1];
   char               *inputValue = NULL;  /* pointer to input value */
   FILE               *tempInFile = NULL;  /* pointer to new input file */
   char               *tempLanguage = NULL;/* pointer to language */
   FILE               *tempOutFile = NULL; /* pointer to new output file */
   char               *tempOutFileName = NULL; /* pointer to output file name */
   char               *tempPassword = NULL;/* pointer to password */
   char               *tempAuthString = NULL;/* pointer to authentication */
   char               *tempServer = NULL;  /* pointer to server name */
   char               *tempUserName = NULL;/* pointer to user name */
   ThreadControlBlock *threadControlBlockPtr = NULL; /* control block pointer */
#ifdef _WIN32
   HANDLE              threadHandle;
   unsigned            threadId;
#else
   pthread_attr_t     *nullAttr = NULL;
   pthread_t           threadHandle;
#endif /* !_WIN32 */
   ThreadStartInfo    *threadStartInfoPtr; /* start information pointer */
   unsigned long       upperBound;

   DriverPrintHeader((waitFlag) ? "LAUNCH WAITING THREAD" : "LAUNCH THREAD");

   /* get a pointer to the current thread's control block */

   threadControlBlockPtr = (ThreadControlBlock *) GetThreadControlBlockPtr();

   /* determine if there is currently enough space in the thread handle */
   /* array to contain another thread handle                            */

   if (threadControlBlockPtr->numHandles >= threadControlBlockPtr->maxHandles)
   {
      /* we need to allocate the array for the first time or reallocate it */
      /* to make it bigger                                                 */
      
      threadControlBlockPtr->maxHandles += 10;

#ifdef _WIN32
      if (threadControlBlockPtr->threadHandles == NULL)
         threadControlBlockPtr->threadHandles = 
            (HANDLE *) malloc(sizeof(HANDLE) * 
                              threadControlBlockPtr->maxHandles);
      else
         threadControlBlockPtr->threadHandles = 
            (HANDLE *) realloc(threadControlBlockPtr->threadHandles,
                               sizeof(HANDLE) *
                               threadControlBlockPtr->maxHandles);
#else
      if (threadControlBlockPtr->threadHandles == NULL)
         threadControlBlockPtr->threadHandles = 
            (pthread_t *) malloc(sizeof(pthread_t) * 
                                 threadControlBlockPtr->maxHandles);
      else
         threadControlBlockPtr->threadHandles = 
            (pthread_t *) realloc(threadControlBlockPtr->threadHandles,
                                  sizeof(pthread_t) * 
                                  threadControlBlockPtr->maxHandles);
#endif
      if (threadControlBlockPtr->threadHandles == NULL)
      {
         DriverPrintError(" **** malloc/realloc error creating thread handle array\n");
         return;
      }
   }

   /* get the filename of the required input file */

   inputValue = GetChar("Filename of input file (): ", "");
   if (strlen(inputValue) == 0)
   {
      DriverPrintError(" **** An input file is required to launch a thread\n");
      return;
   }

   /* open the input file for reading */

   tempInFile = fopen(inputValue, "r");
   if (tempInFile == NULL)
   {
      DriverPrintError(" **** File error during open; input file is invalid\n");
      return;
   }

   /* get the filename of the optional output file */

   inputValue = GetChar("Filename of output file (): ", "");
   if (strlen(inputValue) == 0 || (gQuietMode & SUPPRESS_RESULTS))
   {
      tempOutFile = stdout;
      tempOutFileName = NULL;
   }
   else
   {
      /* build a file name */

      sprintf(filename, "%s%s%s", gResultDir,
              (gResultDir[0] != '\0') ? "\\" : "", inputValue);

      /* open the output file for writing */

      tempOutFile = fopen(filename, "w");
      if (tempOutFile == NULL)
      {
         DriverPrintError(
                    " **** File error during open; output file is invalid\n");
         (void) fclose(tempInFile);

         return;
      }

      tempOutFileName = (char *) malloc(strlen(filename) + 1);
      if (tempOutFileName == NULL)
      {
         DriverPrintError(" **** malloc error creating file name buffer\n");
         (void) fclose(tempInFile);
         if (tempOutFile != stdout)
            (void) fclose(tempOutFile);
         return;
      }

      strcpy(tempOutFileName, filename);
   }

   /* get the optional authentication string that the launched thread will login with */

   inputValue = GetChar("Authentication string (): ", "");
   if (strlen(inputValue) == 0)
      tempAuthString = NULL;
   else
   {
      tempAuthString = (char *) malloc (strlen(inputValue) + 1);
      if (tempAuthString == NULL)
      {
         DriverPrintError(" **** malloc error creating language buffer\n");
         (void) fclose(tempInFile);
         if (tempOutFile != stdout)
            (void) fclose(tempOutFile);
         if (tempOutFileName)
            free(tempOutFileName);
         if (tempUserName)
            free(tempUserName);
         if (tempAuthString)
            free(tempAuthString);
         if (tempPassword)
            free(tempPassword);
         return;
      }

      strcpy(tempAuthString, inputValue);
   }

   /* get the optional user name that the launched thread will login with */
   inputValue = GetChar("User name (): ", "");
   if (strlen(inputValue) == 0)
      tempUserName = NULL;
   else
   {                          /* Save the user name */
      tempUserName = (char *) malloc (strlen(inputValue) + 1);
      if (tempUserName == NULL)
      {
         DriverPrintError(" **** malloc error creating user name buffer\n");
         (void) fclose(tempInFile);
         if (tempOutFile != stdout)
            (void) fclose(tempOutFile);
         if (tempOutFileName)
            free(tempOutFileName);
         return;
      }
      strcpy(tempUserName, inputValue);
   }

   /* get the optional password that the launched thread will login with */

   inputValue = GetChar("Password (): ", "");
   if (strlen(inputValue) == 0)
      tempPassword = NULL;
   else
   {
      tempPassword = (char *) malloc (strlen(inputValue) + 1);
      if (tempPassword == NULL)
      {
         DriverPrintError(" **** malloc error creating password buffer\n");
         (void) fclose(tempInFile);
         if (tempOutFile != stdout)
            (void) fclose(tempOutFile);
         if (tempOutFileName)
            free(tempOutFileName);
         if (tempUserName)
            free(tempUserName);
         return;
      }

      strcpy(tempPassword, inputValue);
   }

   /* get the optional language that the launched thread will login with */

   inputValue = GetChar("Language (): ", "");
   if (strlen(inputValue) == 0)
      tempLanguage = NULL;
   else
   {
      tempLanguage = (char *) malloc (strlen(inputValue) + 1);
      if (tempLanguage == NULL)
      {
         DriverPrintError(" **** malloc error creating language buffer\n");
         (void) fclose(tempInFile);
         if (tempOutFile != stdout)
            (void) fclose(tempOutFile);
         if (tempOutFileName)
            free(tempOutFileName);
         if (tempUserName)
            free(tempUserName);
         if (tempAuthString)
            free(tempAuthString);
         if (tempPassword)
            free(tempPassword);
         return;
      }

      strcpy(tempLanguage, inputValue);
   }

   /* get the optional server that the launched thread will login with */

   inputValue = GetChar("Server (): ", "");
   if (strlen(inputValue) == 0)
      tempServer = NULL;
   else
   {
      tempServer = (char *) malloc (strlen(inputValue) + 1);
      if (tempServer == NULL)
      {
         DriverPrintError(" **** malloc error creating server buffer\n");
         (void) fclose(tempInFile);
         if (tempOutFile != stdout)
            (void) fclose(tempOutFile);
         if (tempOutFileName)
            free(tempOutFileName);
         if (tempUserName)
            free(tempUserName);
         if (tempAuthString)
            free(tempAuthString);
         if (tempPassword)
            free(tempPassword);
         if (tempLanguage)
            free(tempLanguage);
         return;
      }

      strcpy(tempServer, inputValue);
   }

   /* get the optional upper range value for a delay at the start of the */
   /* launched thread                                                    */

   upperBound = (unsigned long) GetLong("Thread startup sleep range (0): ", 0);

   /* allocate a thread start info structure that will be used to facilitate */
   /* the transfer of the pointers to the newly created thread               */

   threadStartInfoPtr = (ThreadStartInfo *) malloc (sizeof(ThreadStartInfo));
   if (threadStartInfoPtr == NULL)
   {
      DriverPrintError(" **** malloc error creating thread start info\n");
      (void) fclose(tempInFile);
      if (tempOutFile != stdout)
         (void) fclose(tempOutFile);
      if (tempOutFileName)
         free(tempOutFileName);
      if (tempUserName)
         free(tempUserName);
      if (tempAuthString)
         free(tempAuthString);
      if (tempPassword)
         free(tempPassword);
      if (tempLanguage)
         free(tempLanguage);
      if (tempServer)
         free(tempServer);
      return;
   }

   /* place the file and login pointers in the thread start info structure */

   threadStartInfoPtr->inFile      = tempInFile;
   threadStartInfoPtr->outFile     = tempOutFile;
   threadStartInfoPtr->outFileName = tempOutFileName;
   threadStartInfoPtr->userName    = tempUserName;
   threadStartInfoPtr->authString   = tempAuthString;
   threadStartInfoPtr->password    = tempPassword;
   threadStartInfoPtr->language    = tempLanguage;
   threadStartInfoPtr->server      = tempServer;
   threadStartInfoPtr->upperBound  = upperBound;

   /* place the synchronization info in the thread start info structure if */
   /* the launched thread is expected to wait                              */

   threadStartInfoPtr->waitFlag = waitFlag;

   if (waitFlag)
   {
      threadStartInfoPtr->launchWaitEventPtr =
         &threadControlBlockPtr->launchWaitEvent;
      threadStartInfoPtr->releaseWaitEventPtr =
         &threadControlBlockPtr->releaseWaitEvent;
   }

   /* start the new thread passing the information to the start function */

#ifdef _WIN32
   if ((threadHandle = (HANDLE) _beginthreadex(NULL, 100,
                                               (unsigned int 
                                                (__stdcall *)(void *))
                                               ThreadStartFunction,
                                               (void *) threadStartInfoPtr, 0,
                                               &threadId)) == 0)
#else
   if (waitFlag)
      DriverLockEvent(&threadControlBlockPtr->launchWaitEvent);

   if (pthread_create(&threadHandle, nullAttr, ThreadStartFunction,
                      (void *) threadStartInfoPtr) != 0)
#endif /* _WIN32 */
   {
      DriverPrintError(" **** unable to start a new thread\n");
      (void) fclose(tempInFile);
      if (tempOutFile != stdout)
         (void) fclose(tempOutFile);
      if (tempOutFileName)
         free(tempOutFileName);
      if (tempUserName)
         free(tempUserName);
      if (tempAuthString)
         free(tempAuthString);
      if (tempPassword)
         free(tempPassword);
      if (tempLanguage)
         free(tempLanguage);
      if (tempServer)
         free(tempServer);
      if (threadStartInfoPtr)
         free(threadStartInfoPtr);
#ifndef _WIN32
      if (waitFlag)
         DriverUnlockEvent(&threadControlBlockPtr->launchWaitEvent);
#endif
      return;
   }

   /* if appropriate wait until the launched thread has communicated that it */
   /* is poised and ready to start reading its command file when released    */

   if (waitFlag)
   {
      DriverWaitForEvent(&threadControlBlockPtr->launchWaitEvent);
      DriverUnlockEvent(&threadControlBlockPtr->launchWaitEvent);
   }

   /* save the thread handle for the newly created thread and increment */
   /* the number of handles                                             */

   threadControlBlockPtr->threadHandles[threadControlBlockPtr->
                                        numHandles++] = threadHandle;
}


/*****************************************************************************/
/*                                                                           */
/*                                     main                                  */
/*                                                                           */
/*****************************************************************************/

int main(argc, argv)
int     argc;        /* IN; number of items in command line */
char   *argv[];      /* IN; array of command line arguments */

{
   ThreadControlBlock * threadControlBlockPtr; /* control block pointer */

#ifdef _WIN32
   /* make console big enough for the command menu */

   SetConsoleSize();

   /* allow more files than the default value */

   _setmaxstdio(2048);
#endif /* _WIN32 */

   gRandomNumberSeed = (unsigned int) time(NULL);

   if (!EstablishThreadEnvironment())
      return -1;

   if ((threadControlBlockPtr = CreateThreadControlBlock()) == NULL)
      return -1;
      
   threadControlBlockPtr->inFile[0] = stdin; 
   threadControlBlockPtr->outFile = stdout;

   threadControlBlockPtr->primaryThread = 1;

   if (!ProcessCommandLine(argc, argv))
      return -1;

   (void) printf("\n     AR System API Driver\n");
   PrintMainMenu();

   InitCommandProcessing();

   ProcessCommands();

   DestroyThreadControlBlock();

   TermCommandProcessing();

   CleanupThreadEnvironment();

   return 0;
}
