/* 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 "cvariety.h"
#include "arvers.h"
#else /* AR_VERS_H_FILE */
#define AR_VERSION_DRIVER "7.0.1.003"
#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

#ifndef EXTERNAL_MODE
ARBoolean ExtCommandProcessing(command)
char  *command;  /* IN; command string */
{
   /* perform any external command processing here */

   return FALSE;
}
#else
ARBoolean ExtCommandProcessing();
#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 = 52;
   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=0;        /* 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();
   strcpy(threadControlBlockPtr->control.localeInfo.charSet, "");

   for (i = 1; i < argc; i++)
   {                              /* process each entry on the command line */
      unsigned int maxArgumentLen = 0;

      if (argv[i][0] == '-' && strchr("-Uacdglopqstux", argv[i][1]))
         option = argv[i][1];
      else
      {                           /* unrecognized option */
         DriverPrintError("Unrecognized option %s\n", argv[i]);
         return FALSE;
      }

      if (strchr("U", option))    /* option takes no arg */
         tempPtr = "";
      else if (argv[i][2])        /* -oFOO */
         tempPtr = &(argv[i][2]);
      else
      {                           /* -o FOO */
         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 */
      maxArgumentLen = 0;
      switch (option)
      {
         case 'u' :
            maxArgumentLen = AR_MAX_ACCESS_NAME_SIZE;
            break;
         case 'p' :
            maxArgumentLen = AR_MAX_PASSWORD_SIZE;
            break;
         case 'a' :
            maxArgumentLen = AR_MAX_AUTH_SIZE;
            break;
         case 's' :
            maxArgumentLen = AR_MAX_SERVER_SIZE;
            break;
         case 't' :
            maxArgumentLen = AR_MAX_LANG_SIZE;
            break;
         case 'l' :
            maxArgumentLen = AR_MAX_LOCALE_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;
         default:
            maxArgumentLen = AR_MAX_SERVER_SIZE;
            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.localeInfo.locale, tempPtr);
            break;
         case '-':
            if (strcmp(tempPtr, "unicode") != 0)
            {
               DriverPrintError("Unrecognized option --%s\n", tempPtr);
               return FALSE;
            }
            /* FALL THROUGH TO case 'U': */
         case 'U' :
            strcpy(threadControlBlockPtr->control.localeInfo.charSet, "UTF-8");
            break;
         case 't' :
            (void) strcpy(threadControlBlockPtr->control.localeInfo.timeZone, 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)  getlistblk(gleb)\n");
   DriverPrintMenu(" getmult(gmal) getmult(gmes)   getmult(gmf)  service  (sve)  getlistw/f(glewf)\n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" Char Menu     Schema          License       Schema Field    VUI\n");
   DriverPrintMenu(" ---------     ------          -------       ------------    ---\n");
   DriverPrintMenu(" get    (gc)   get    (gs)     val    (vl)   get     (gsf)   get    (gv)\n");
   DriverPrintMenu(" set    (sc)   set    (ss)     valmult(vml)  set     (ssf)   set    (sv)\n");
   DriverPrintMenu(" create (cc)   create (cs)     create (cl)   create  (csf)   create (cv)\n");
   DriverPrintMenu(" delete (dc)   delete (ds)     delete (dl)   delete  (dsf)   delete (dv)\n");
   DriverPrintMenu(" getlist(glc)  getlist(gls)    getlist(gll)  getlist (glsf)  getlist(glsv)\n");
   DriverPrintMenu(" getmult(gmc)  gls w/a(glsa)   import (iml)  getmult (gmsf)  getmult(gmv)\n");
   DriverPrintMenu(" expand (ec)   getmult(gms)    export (exl)  delmult (dmsf)\n");
   DriverPrintMenu(" exp ssm(essm) getlist ext(glxsc)            getmult ext cands(gmxfc)\n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" Container     Alert           Info          Control/Logging Thread/Timer\n");
   DriverPrintMenu(" ---------     -----           ----          --------------- ------------\n");
   DriverPrintMenu(" get    (gco)  create   (cae)  get svr (gsi) record   (rec)  launch      (lt)\n");
   DriverPrintMenu(" set    (sco)  register (rfa)  set svr (ssi) stop rec (srec) launch wait (lwt)\n");
   DriverPrintMenu(" create (cco)  deregistr(dfa)  get FT  (gft) open out (oout) release wait(rwt)\n");
   DriverPrintMenu(" delete (dco)  gla user (glau) set FT  (sft) close out(cout) sleep       (st)\n");
   DriverPrintMenu(" getlist(glco) get count(gac)  get stat(gss) execute  (ex)   random sleep(rst)\n");
   DriverPrintMenu(" getmult(gmco) decode   (dcam) srvr chr(gscs)bgn loop (bl)   msec sleep  (msst)\n");
   DriverPrintMenu("                               clnt chr(gccs)end loop (el)\n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" Init/Term     Encode/Decode   Misc Lists    Misc            Misc\n");
   DriverPrintMenu(" ---------     -------------   ----------    ----            ----\n");
   DriverPrintMenu(" init   (init) enquery(ecqal)  server(svr)   ver user (ver)  get file   (gfl)\n");
   DriverPrintMenu(" term   (term) dequery(dcqal)  group (glg)   export   (exp/f)set file   (sfl)\n");
   DriverPrintMenu(" help   (h, ?) enassig(ecasn)  user  (glu)   import   (imp)  get errmsg (gem)\n");
   DriverPrintMenu(" exit   (e, q) deassig(dcasn)  sql   (glsql) unimport (unimp)set logging(slog)\n");
   DriverPrintMenu(" login  (log)  echstry(echst)  sql al(sqlal) exec proc(proc) close conn (cnc)\n");
   DriverPrintMenu(" dr ver (dver) ecdiary(ecdia)  role  (glr)   exec p al(epal) valid cache(vfc)\n");
   DriverPrintMenu(" bgntran(bbet) ecdate (ecdat)                load qual(lqs)  signal     (sig)\n");
   DriverPrintMenu(" endtran(ebet) dcdate (dcdat)                set port (ssp)  getmult ep (gmep)\n");
   DriverPrintMenu("                                                             imper user (imusr)\n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" XML           Session Config  Localized Val App States      Currency Ratio\n");
   DriverPrintMenu(" ------------- --------------  ------------- ----------      --------------\n");
   DriverPrintMenu(" get   (xmlge) get conf(gsc)   get    (glv)  get     (gas)   getmult sets(gmcrs)\n");
   DriverPrintMenu(" set   (xmlse) set conf(ssc)   getmult(gmlv) set     (sas)   get ratio   (gcr)\n");
   DriverPrintMenu(" create(xmlce)                               getlist (glas)\n");
   DriverPrintMenu(" parse (xmlpd)\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';

   DriverPrintPrompt("\nCommand: ");

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

      GetInputLine();
      
      if ((threadControlBlockPtr->buffer[0] == '\0') ||
          (threadControlBlockPtr->buffer[0] == '#'))
         DriverPrintPrompt("\nCommand: "); /* blank line or comment */
      else
      {                                    /* isolate any arguments */
         *args = NULL;
         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) > (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 if (ExtCommandProcessing(threadControlBlockPtr->buffer) == FALSE)
            {
               DriverPrintError(" *** Command not recognized ***\n");
               DriverPrintPrompt("\nCommand: ");
            }
         }
      }
   }

   return commandCode;
}


/*****************************************************************************/
/*                                                                           */
/*                            PrintDriverVersion                             */
/*                                                                           */
/*****************************************************************************/

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

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


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

void ProcessCommands()

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

   /* 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_MULTIPLE_FILTERS:
            APIARGetMultipleFilters();
            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_GET_MULTIPLE_ESCALATIONS:
            APIARGetMultipleEscalations();
            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_MULTIPLE_CHAR_MENUS:
            APIARGetMultipleCharMenus();
            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_GET_MULTIPLE_VUIS:
            APIARGetMultipleVUIs();
            break;
         case COMMAND_EXPORT:
            APIARExport();
            break;
         case COMMAND_IMPORT:
            APIARImport();
            break;
         case COMMAND_UNIMPORT:
            APIARUnImport();
            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_GETMULT_CONTAINER:
            APIARGetMultipleContainers();
            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_IMPORT_LICENSE:
            APIARImportLicense();
            break;
         case COMMAND_EXPORT_LICENSE:
            APIARExportLicense();
            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;
         case COMMAND_ENCODE_DATE:
            APIARDateToJulianDate();
            break;
         case COMMAND_DECODE_DATE:
            APIARJulianDateToDate();
            break;
         case COMMAND_XML_CREATE_ENTRY:
            APIARXMLCreateEntry();
            break;
         case COMMAND_XML_GET_ENTRY:
            APIARXMLGetEntry();
            break;
         case COMMAND_XML_SET_ENTRY:
            APIARXMLSetEntry();
            break;
         case COMMAND_GET_MULT_CURR_RATIO_SETS:
            APIARGetMultipleCurrencyRatioSets();
            break;
         case COMMAND_GET_CURRENCY_RATIO:
            APIARGetCurrencyRatio();
            break;
         case COMMAND_GET_MULTIPLE_ENTRYPOINTS:
            APIARGetMultipleEntryPoints();
            break;
         case COMMAND_GETLIST_ROLE:
            APIARGetListRole();
            break;
         case COMMAND_GETLIST_APPLICATION_STATES:
            APIARGetListApplicationState();
            break;
         case COMMAND_GET_APPLICATION_STATE:
            APIARGetApplicationState();
            break;
         case COMMAND_SET_APPLICATION_STATE:
            APIARSetApplicationState();
            break;
         case COMMAND_BEGIN_BULK_ENTRY_TXN:
            APIARBeginBulkEntryTransaction();
            break;
         case COMMAND_END_BULK_ENTRY_TXN:
            APIAREndBulkEntryTransaction();
            break;
         case COMMAND_GETLIST_ENTRY_BLOCKS:
            APIARGetListEntryBlocks();
            break;
         case COMMAND_SET_IMPERSONATED_USER:
            APIARSetImpersonatedUser();
            break;
         case COMMAND_SERVICE_ENTRY:
            APIARServiceEntry();
            break;
         case COMMAND_EXPORT_TO_FILE:
            APIARExportToFile();
            break;
         case COMMAND_GET_CLIENT_CHARSET:
            APIARGetClientCharSet();
            break;
         case COMMAND_GET_SERVER_CHARSET:
            APIARGetServerCharSet();
            break;
         case COMMAND_XML_PARSE_DOCUMENT:
            APIARParseXMLDocument();
            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->locale)
      strcpy(threadControlBlockPtr->control.localeInfo.locale,
             threadStartInfoPtr->locale);
   if (threadStartInfoPtr->charSet)
      strcpy(threadControlBlockPtr->control.localeInfo.charSet,
             threadStartInfoPtr->charSet);
   if (threadStartInfoPtr->timeZone)
      strcpy(threadControlBlockPtr->control.localeInfo.timeZone,
             threadStartInfoPtr->timeZone);
   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->authString)
      free(threadStartInfoPtr->authString);
   if (threadStartInfoPtr->userName)
      free(threadStartInfoPtr->userName);
   if (threadStartInfoPtr->password)
      free(threadStartInfoPtr->password);
   if (threadStartInfoPtr->locale)
      free(threadStartInfoPtr->locale);
   if (threadStartInfoPtr->timeZone)
      free(threadStartInfoPtr->timeZone);
   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 */
   char               *tempAuthString = NULL; /* pointer to auth string */
   FILE               *tempInFile = NULL;     /* pointer to new input file */
   char               *tempLocale = NULL;     /* pointer to locale */
   FILE               *tempOutFile = NULL;    /* pointer to new output file */
   char               *tempOutFileName = NULL;/* pointer to output file name */
   char               *tempPassword = NULL;   /* pointer to password */
   char               *tempServer = NULL;     /* pointer to server name */
   char               *tempTimeZone = NULL;   /* pointer to time zone */
   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");
         fclose(tempInFile);

         return;
      }

      tempOutFileName = (char *) malloc(strlen(filename) + 1);
      if (tempOutFileName == NULL)
      {
         DriverPrintError(" **** malloc error creating file name buffer\n");
         fclose(tempInFile);
         if (tempOutFile != stdout)
            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 auth string buffer\n");
         fclose(tempInFile);
         if (tempOutFile != stdout)
            fclose(tempOutFile);
         if (tempOutFileName)
            free(tempOutFileName);
         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");
         fclose(tempInFile);
         if (tempOutFile != stdout)
            fclose(tempOutFile);
         if (tempOutFileName)
            free(tempOutFileName);
         if (tempAuthString)
            free(tempAuthString);
         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");
         fclose(tempInFile);
         if (tempOutFile != stdout)
            fclose(tempOutFile);
         if (tempOutFileName)
            free(tempOutFileName);
         if (tempAuthString)
            free(tempAuthString);
         if (tempUserName)
            free(tempUserName);
         return;
      }

      strcpy(tempPassword, inputValue);
   }

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

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

      strcpy(tempLocale, inputValue);
   }

   /* get the optional time zone that the launched thread will login with */

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

      strcpy(tempTimeZone, 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");
         fclose(tempInFile);
         if (tempOutFile != stdout)
            fclose(tempOutFile);
         if (tempOutFileName)
            free(tempOutFileName);
         if (tempAuthString)
            free(tempAuthString);
         if (tempUserName)
            free(tempUserName);
         if (tempPassword)
            free(tempPassword);
         if (tempLocale)
            free(tempLocale);
         if (tempTimeZone)
            free(tempTimeZone);
         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");
      fclose(tempInFile);
      if (tempOutFile != stdout)
         fclose(tempOutFile);
      if (tempOutFileName)
         free(tempOutFileName);
      if (tempAuthString)
         free(tempAuthString);
      if (tempUserName)
         free(tempUserName);
      if (tempPassword)
         free(tempPassword);
      if (tempLocale)
         free(tempLocale);
      if (tempTimeZone)
         free(tempTimeZone);
      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->authString  = tempAuthString;
   threadStartInfoPtr->userName    = tempUserName;
   threadStartInfoPtr->password    = tempPassword;
   threadStartInfoPtr->locale      = tempLocale;
   if (tempLocale)
   {
      /*
       * If locale postfixes a charset (e.g., "en_US.UTF-8") split that out
       * NB we steal the space: don't free(threadStartInfoPtr->charSet)
       */
      size_t n = strcspn(tempLocale, ".;");
      if (tempLocale[n]=='\0')
         threadStartInfoPtr->charSet = NULL;
      {
         char *charSet = &tempLocale[n];
         *charSet++ = '\0';  /* skip the '.' */
         threadStartInfoPtr->charSet = charSet;
      }
   }
   else
      threadStartInfoPtr->charSet = NULL;

   threadStartInfoPtr->timeZone    = tempTimeZone;
   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");
      fclose(tempInFile);
      if (tempOutFile != stdout)
         fclose(tempOutFile);
      if (tempOutFileName)
         free(tempOutFileName);
      if (tempAuthString)
         free(tempAuthString);
      if (tempUserName)
         free(tempUserName);
      if (tempPassword)
         free(tempPassword);
      if (tempLocale)
         free(tempLocale);
      if (tempTimeZone)
         free(tempTimeZone);
      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;
}
