/* File:  main.c */

/*****************************************************************************/
/*                                                                           */
/*                                                                           */
/*   ©Copyright  2009 – 2012 BMC Software, Inc.                              */
/*   BMC, BMC Software, the BMC logos and other BMC marks are trademarks or  */
/*   registered trademarks of BMC Software, Inc. in the U.S. and /or         */
/*   certain other countries.                                                */
/*****************************************************************************/

#define MAIN

#ifdef _WIN32
#include <windows.h>
#include <io.h>
#include <process.h>
#ifndef snprintf
#define  snprintf _snprintf
#endif
#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 "8.1.02 201408260235"
#define AR_VERSION_ARS  AR_VERSION_DRIVER
#endif /* AR_VERS_H_FILE */

int 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 */
}
#endif

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

   return FALSE;
}
#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 = 56;
   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 */
   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("-UacdglopqstuxSP", argv[i][1]))
         option = argv[i][1];
      else if (argv[i][0] == '-' && strcmp(&argv[i][1], "version") == 0)
      {
         /* application is launched only for version info */
         /* so quit the program after processing          */
         fprintf(stdout, "AR API Driver version %s\n", AR_VERSION_ARS);
         exit(gSavedExitCode);
      }
      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;
         case 'S' :
            maxArgumentLen = 6;
         case 'P' :
            maxArgumentLen = 5;
         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 && strcmp(tempPtr, "exitOnError") != 0)
            {
               DriverPrintError("Unrecognized option --%s\n", tempPtr);
               return FALSE;
            }
            if (strcmp(tempPtr, "exitOnError") == 0)
            {
                gExitOnError = TRUE;
                break;
            }
            /* 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' :
            OpenInputFile(tempPtr,TRUE);
            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;
         case 'S' :
            gSocketNumber = (unsigned int) atol(tempPtr);
            gSetServerPort = TRUE;
            break;
         case 'P' :
            gPortNumber = (unsigned int) atol(tempPtr);
            gSetServerPort = TRUE;
            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)   stats     (stat)\n");
   DriverPrintMenu(" set    (sal)  set    (ses)    set    (sf)   set      (se)   get BLOB  (geb)\n");
   DriverPrintMenu(" create (cal)  create (ces)    create (cf)   create   (ce)   getmult   (gme)\n");
   DriverPrintMenu(" delete (dal)  delete (des)    delete (df)   merge    {me)   getlistblk(gleb)\n");
   DriverPrintMenu(" getlist(glal) getlist(gles)   getlist(glf)  delete   (de)   getlistw/f(glewf)\n");
   DriverPrintMenu(" getmult(gmal) getmult(gmes)   getmult(gmf)  getlist  (gle)  getonew/f (goewf)\n");
   DriverPrintMenu("               run    (resc)                 service  (sve)  gle w/msf (glmsf)\n");
   DriverPrintMenu("                                             setget   (sge)                   \n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" Char Menu     Schema          License       VUI             Encode/Decode\n");
   DriverPrintMenu(" ---------     ------          -------       ---             -------------\n");
   DriverPrintMenu(" get    (gc)   get    (gs)     val    (vl)   get     (gv)    enc query(ecqal)\n");
   DriverPrintMenu(" set    (sc)   set    (ss)     valmult(vml)  set     (sv)    dec query(dcqal)\n");
   DriverPrintMenu(" create (cc)   create (cs)     create (cl)   create  (cv)    enc assig(ecasn)\n");
   DriverPrintMenu(" delete (dc)   delete (ds)     delete (dl)   delete  (dv)    dec assig(dcasn)\n");
   DriverPrintMenu(" getlist(glc)  getlist(gls)    getlist(gll)  getlist (glsv)  enc hstry(echst)\n");
   DriverPrintMenu(" getmult(gmc)  gls w/a(glsa)   import (iml)  getmult (gmv)   enc diary(ecdia)\n");
   DriverPrintMenu(" expand (ec)   getmult(gms)    export (exl)                  enc date (ecdat)\n");
   DriverPrintMenu(" exp ssm(essm) getlist ext(glxsc)                            dec date (dcdat)\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 stat(gss) open out (oout) release wait(rwt)\n");
   DriverPrintMenu(" delete (dco)  gla user (glau) srvr chr(gscs)close out(cout) sleep       (st)\n");
   DriverPrintMenu(" getlist(glco) get count(gac)  clnt chr(gccs)execute  (ex)   random sleep(rst)\n");
   DriverPrintMenu(" getmult(gmco) decode   (dcam)               bgn loop (bl)   msec sleep  (msst)\n");
   DriverPrintMenu("                                             end loop (el)\n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" Schema Field   Init/Term      Misc Lists    Misc            Misc\n");
   DriverPrintMenu(" ------------  ------------    ----------    ----            ----\n");
   DriverPrintMenu(" get     (gsf)  init    (init) server(svr)   ver user (ver/2)get file   (gfl)\n");
   DriverPrintMenu(" set     (ssf)  term    (term) group (glg)   export   (exp/f)set file   (sfl)\n");
   DriverPrintMenu(" create  (csf)  help    (h, ?) user  (glu)   import   (imp)  get errmsg (gem)\n");
   DriverPrintMenu(" delete  (dsf)  exit    (e, q) sql   (glsql) unimport (unimp)set logging(slog)\n");
   DriverPrintMenu(" getlist (glsf) login   (log)  sql al(sqlal) exec proc(proc) close conn (cnc)\n");
   DriverPrintMenu(" getmult (gmsf) dr ver  (dver) role  (glr)   exec p al(epal) valid cache(vfc)\n");
   DriverPrintMenu(" setmult (smsf) getconf (gsc)                load qual(lqs)  signal     (sig)\n");
   DriverPrintMenu(" crtmult (cmsf) setconf (ssc)                set port (ssp)  getmult ep (gmep)\n");
   DriverPrintMenu(" delmult (dmsf)                              obj chgs (goct) imper user (imusr)\n");
   DriverPrintMenu(" getmult ext cands(gmxfc)                    get cache event (gce)\n");
   DriverPrintMenu(" set w/file image (ssfwi)                    create overlay  (co)\n");
   DriverPrintMenu(" create w/file image (csfwi)                 create overlay from obj (cofo)\n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" XML            Transaction    Image         Workflow Debug Commands  \n");
   DriverPrintMenu(" ---            -----------    -----         -----------------------  \n");
   DriverPrintMenu(" get    (xmlge) bgnclnt (bcmt) get     (gi)  cur loc  (dbcl) get KW   (dbkw)\n");
   DriverPrintMenu(" set    (xmlse) endclnt (ecmt) set     (si)  execute  (dbex) user ctxt(dbuc)\n");
   DriverPrintMenu(" create (xmlce) setclnt (scmt) create  (ci)  get FVL  (dbgf) set bkp  (dbbp)\n");
   DriverPrintMenu(" service(xmlsv) relclnt (rcmt) delete  (di)  set FVL  (dbsf) bp clr   (dbbc)\n");
   DriverPrintMenu(" parse  (xmlpd) bgnentry(bbet) getlist (gli) get qual (dbgq) bp lst   (dbbl)\n");
   DriverPrintMenu("                endentry(ebet) getmult (gmi) set qual (dbsq) clr all  (dbbx)\n");
   DriverPrintMenu("                                             get mode (dbgm) term API (dbta)\n");
   DriverPrintMenu("                                             set mode (dbsm)                \n");
   DriverPrintMenu("\n");
   DriverPrintMenu(" Localized Val  Currency Ratio App States\n");
   DriverPrintMenu(" -------------  -------------- ----------\n");
   DriverPrintMenu(" get    (glv)   get    (gcr)   get    (gas)\n");
   DriverPrintMenu(" getmult(gmlv)  getmult(gmcrs) set    (sas)\n");
   DriverPrintMenu("                               getlist(glas)\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 */
   int    returnCode = AR_RETURN_OK;

   /* process commands until exit specified */

   while ((commandCode = GetNextCommand(&args)) != COMMAND_EXIT)
   {
      switch (commandCode)
      {
         case COMMAND_LOGIN:
            GetARControlStruct();
            break;
         case COMMAND_GET_ENTRY:
            returnCode = APIARGetEntry();
            break;
         case COMMAND_SET_ENTRY:
            returnCode = APIARSetEntry();
            break;
         case COMMAND_CREATE_ENTRY:
            returnCode = APIARCreateEntry();
            break;
         case COMMAND_DELETE_ENTRY:
            returnCode = APIARDeleteEntry();
            break;
         case COMMAND_GETLIST_ENTRY:
            returnCode = APIARGetListEntry();
            break;
         case COMMAND_GETLIST_ENTRY_WITH_FIELDS:
            returnCode = APIARGetListEntryWithFields();
            break;
         case COMMAND_GET_FILTER:
            returnCode = APIARGetFilter();
            break;
         case COMMAND_SET_FILTER:
            returnCode = APIARSetFilter();
            break;
         case COMMAND_CREATE_FILTER:
            returnCode = APIARCreateFilter();
            break;
         case COMMAND_DELETE_FILTER:
            returnCode = APIARDeleteFilter();
            break;
         case COMMAND_GETLIST_FILTER:
            returnCode = APIARGetListFilter();
            break;
         case COMMAND_GET_MULTIPLE_FILTERS:
            returnCode = APIARGetMultipleFilters();
            break;
         case COMMAND_GET_ESCALATION:
            returnCode = APIARGetEscalation();
            break;
         case COMMAND_SET_ESCALATION:
            returnCode = APIARSetEscalation();
            break;
         case COMMAND_CREATE_ESCALATION:
            returnCode = APIARCreateEscalation();
            break;
         case COMMAND_DELETE_ESCALATION:
            returnCode = APIARDeleteEscalation();
            break;
         case COMMAND_GETLIST_ESCALATION:
            returnCode = APIARGetListEscalation();
            break;
         case COMMAND_GET_MULTIPLE_ESCALATIONS:
            returnCode = APIARGetMultipleEscalations();
            break;
         case COMMAND_GETLIST_GROUP:
            returnCode = APIARGetListGroup();
            break;
         case COMMAND_GET_SCHEMA:
            returnCode = APIARGetSchema();
            break;
         case COMMAND_SET_SCHEMA:
            returnCode = APIARSetSchema();
            break;
         case COMMAND_CREATE_SCHEMA:
            returnCode = APIARCreateSchema();
            break;
         case COMMAND_DELETE_SCHEMA:
            returnCode = APIARDeleteSchema();
            break;
         case COMMAND_GETLIST_SCHEMA:
            returnCode = APIARGetListSchema();
            break;
         case COMMAND_GETMULT_SCHEMA:
            returnCode = APIARGetMultipleSchemas();
            break;
         case COMMAND_GET_SCH_FIELD:
            returnCode = APIARGetField();
            break;
         case COMMAND_SET_SCH_FIELD:
            returnCode = APIARSetField(FALSE);
            break;
         case COMMAND_CREATE_SCH_FIELD:
            returnCode = APIARCreateField(FALSE);
            break;
         case COMMAND_DELETE_SCH_FIELD:
            returnCode = APIARDeleteField();
            break;
         case COMMAND_GETLIST_SCH_FIELD:
            returnCode = APIARGetListField();
            break;
         case COMMAND_GET_CHAR_MENU:
            returnCode = APIARGetCharMenu();
            break;
         case COMMAND_SET_CHAR_MENU:
            returnCode = APIARSetCharMenu();
            break;
         case COMMAND_CREATE_CHAR_MENU:
            returnCode = APIARCreateCharMenu();
            break;
         case COMMAND_DELETE_CHAR_MENU:
            returnCode = APIARDeleteCharMenu();
            break;
         case COMMAND_GETLIST_CHAR_MENU:
            returnCode = APIARGetListCharMenu();
            break;
         case COMMAND_GET_MULTIPLE_CHAR_MENUS:
            returnCode = APIARGetMultipleCharMenus();
            break;
         case COMMAND_GET_VUI:
            returnCode = APIARGetVUI();
            break;
         case COMMAND_SET_VUI:
            returnCode = APIARSetVUI();
            break;
         case COMMAND_CREATE_VUI:
            returnCode = APIARCreateVUI();
            break;
         case COMMAND_DELETE_VUI:
            returnCode = APIARDeleteVUI();
            break;
         case COMMAND_GETLIST_VUI:
            returnCode = APIARGetListVUI();
            break;
         case COMMAND_GET_MULTIPLE_VUIS:
            returnCode = APIARGetMultipleVUIs();
            break;
         case COMMAND_EXPORT:
            returnCode = APIARExport();
            break;
         case COMMAND_IMPORT:
            returnCode = APIARImport();
            break;
         case COMMAND_UNIMPORT:
            returnCode = APIARUnImport();
            break;
         case COMMAND_GET_SERVER_INFO:
            returnCode = APIARGetServerInfo();
            break;
         case COMMAND_VERIFY_USER:
            returnCode = APIARVerifyUser();
            break;
         case COMMAND_EXECUTE:
            returnCode = OpenInputFile(args,FALSE);
            break;
         case COMMAND_OPEN_OUT:
            returnCode = OpenOutputFile();
            break;
         case COMMAND_CLOSE_OUT:
            returnCode = CloseOutputFile();
            break;
         case COMMAND_RECORD:
            returnCode = StartRecording();
            break;
         case COMMAND_STOP_RECORD:
            StopRecording();
            break;
         case COMMAND_LAUNCH_THREAD:
            returnCode = LaunchThread(FALSE);
            break;
         case COMMAND_LAUNCH_WAITING_THREAD:
            returnCode = LaunchThread(TRUE);
            break;
         case COMMAND_RELEASE_WAITING_THREADS:
            ReleaseWaitingThreads();
            break;
         case COMMAND_SLEEP_TIMER:
            SleepTimer();
            break;
         case COMMAND_RANDOM_SLEEP_TIMER:
            returnCode = RandomSleepTimer();
            break;
         case COMMAND_MILLISECOND_SLEEP_TIMER:
            MillisecondSleepTimer();
            break;
         case COMMAND_BEGIN_LOOP:
            returnCode = BeginLoop();
            break;
         case COMMAND_END_LOOP:
            returnCode = EndLoop();
            break;
         case COMMAND_GETLIST_SERVER:
            returnCode = APIARGetListServer();
            break;
         case COMMAND_INITIALIZATION:
            returnCode = APIARInitialization();
            break;
         case COMMAND_TERMINATION:
            returnCode = APIARTermination();
            break;
         case COMMAND_GET_ACTIVE_LINK:
            returnCode = APIARGetActiveLink();
            break;
         case COMMAND_SET_ACTIVE_LINK:
            returnCode = APIARSetActiveLink();
            break;
         case COMMAND_CREATE_ACTIVE_LINK:
            returnCode = APIARCreateActiveLink();
            break;
         case COMMAND_DELETE_ACTIVE_LINK:
            returnCode = APIARDeleteActiveLink();
            break;
         case COMMAND_GETLIST_ACTIVE_LINK:
            returnCode = APIARGetListActiveLink();
            break;
         case COMMAND_GET_MULTIPLE_ACTIVE_LINKS:
            returnCode = APIARGetMultipleActiveLinks();
            break;
         case COMMAND_MERGE_ENTRY:
            returnCode = APIARMergeEntry();
            break;
         case COMMAND_LOAD_AR_QUAL_STRUCT:
            returnCode = APIARLoadARQualifierStruct();
            break;
         case COMMAND_EXPAND_CHAR_MENU:
            returnCode = APIARExpandCharMenu();
            break;
         case COMMAND_SET_SERVER_INFO:
            returnCode = APIARSetServerInfo();
            break;
         case COMMAND_GETLIST_USER:
            returnCode = APIARGetListUser();
            break;
         case COMMAND_ENTRY_STATISTICS:
            returnCode = APIARGetEntryStatistics();
            break;
         case COMMAND_GET_SERVER_STAT:
            returnCode = APIARGetServerStatistics();
            break;
         case COMMAND_GETLIST_SQL:
            returnCode = APIARGetListSQL();
            break;
         case COMMAND_DELETE_MULTI_FIELD:
            returnCode = APIARDeleteMultipleFields();
            break;
         case COMMAND_EXECUTE_PROCESS:
            returnCode = APIARExecuteProcess();
            break;
         case COMMAND_SET_SERVER_PORT:
            returnCode = APIARSetServerPort(TRUE);
            break;
         case COMMAND_GET_MULTIPLE_ENTRY:
            returnCode = APIARGetMultipleEntries();
            break;
         case COMMAND_GET_SUPPORT_FILE:
            returnCode = APIARGetSupportFile();
            break;
         case COMMAND_SET_SUPPORT_FILE:
            returnCode = APIARSetSupportFile();
            break;
         case COMMAND_CREATE_SUPPORT_FILE:
            returnCode = APIARCreateSupportFile();
            break;
         case COMMAND_DELETE_SUPPORT_FILE:
            returnCode = APIARDeleteSupportFile();
            break;
         case COMMAND_GETLIST_SUPPORT_FILE:
            returnCode = APIARGetListSupportFile();
            break;
         case COMMAND_GETENTRY_BLOB:
            returnCode = APIARGetEntryBLOB();
            break;
         case COMMAND_GET_CONTAINER:
            returnCode = APIARGetContainer();
            break;
         case COMMAND_SET_CONTAINER:
            returnCode = APIARSetContainer();
            break;
         case COMMAND_CREATE_CONTAINER:
            returnCode = APIARCreateContainer();
            break;
         case COMMAND_DELETE_CONTAINER:
            returnCode = APIARDeleteContainer();
            break;
         case COMMAND_GETLIST_CONTAINER:
            returnCode = APIARGetListContainer();
            break;
         case COMMAND_GETMULT_CONTAINER:
            returnCode = APIARGetMultipleContainers();
            break;
         case COMMAND_GET_ERROR_MESSAGE :
            APIARGetTextForErrorMessage();
            break;
         case COMMAND_SET_LOGGING:
            returnCode = APIARSetLogging();
            break;
         case COMMAND_CLOSE_NET_CONNECTIONS:
            returnCode = APIARCloseNetworkConnections();
            break;
         case COMMAND_SIGNAL:
            returnCode = APIARSignal();
            break;
         case COMMAND_VALIDATE_FORM_CACHE:
            returnCode = APIARValidateFormCache();
            break;
         case COMMAND_GET_MULTIPLE_FIELDS:
            returnCode = APIARGetMultipleFields();
            break;
         case COMMAND_GET_LOCALIZED_VALUE:
            returnCode = APIARGetLocalizedValue();
            break;
         case COMMAND_GET_MULT_LOCALIZED_VALUES:
            returnCode = APIARGetMultipleLocalizedValues();
            break;
         case COMMAND_GETLIST_SCHEMA_WITH_ALIAS:
            returnCode = APIARGetListSchemaWithAlias();
            break;
         case COMMAND_CREATE_ALERT_EVENT:
            returnCode = APIARCreateAlertEvent();
            break;
         case COMMAND_REGISTER_ALERTS:
            returnCode = APIARRegisterForAlerts();
            break;
         case COMMAND_DEREGISTER_ALERTS:
            returnCode = APIARDeregisterForAlerts();
            break;
         case COMMAND_GETLIST_ALERT_USER:
            returnCode = APIARGetListAlertUser();
            break;
         case COMMAND_GET_ALERT_COUNT:
            returnCode = APIARGetAlertCount();
            break;
         case COMMAND_DECODE_ALERT_MESSAGE:
            returnCode = APIARDecodeAlertMessage();
            break;
         case COMMAND_ENCODE_QUALIFIER:
            returnCode = APIAREncodeARQualifierStruct();
            break;
         case COMMAND_DECODE_QUALIFIER:
            returnCode = APIARDecodeARQualifierStruct();
            break;
         case COMMAND_ENCODE_ASSIGN:
            returnCode = APIAREncodeARAssignStruct();
            break;
         case COMMAND_DECODE_ASSIGN:
            returnCode = APIARDecodeARAssignStruct();
            break;
         case COMMAND_ENCODE_HISTORY:
            returnCode = APIAREncodeStatusHistory();
            break;
         case COMMAND_ENCODE_DIARY:
            returnCode = APIAREncodeDiary();
            break;
         case COMMAND_GETLIST_EXT_SCHEMA_CANDS:
            returnCode = APIARGetListExtSchemaCandidates();
            break;
         case COMMAND_GET_MULT_EXT_FIELD_CANDS:
            returnCode = APIARGetMultipleExtFieldCandidates();
            break;
         case COMMAND_EXPAND_SS_MENU:
            returnCode = APIARExpandSSMenu();
            break;
         case COMMAND_VALIDATE_LICENSE:
            returnCode = APIARValidateLicense();
            break;
         case COMMAND_VALIDATE_MULTIPLE_LICENSES:
            returnCode = APIARValidateMultipleLicenses();
            break;
         case COMMAND_GETLIST_LICENSE:
            returnCode = APIARGetListLicense();
            break;
         case COMMAND_CREATE_LICENSE:
            returnCode = APIARCreateLicense();
            break;
         case COMMAND_DELETE_LICENSE:
            returnCode = APIARDeleteLicense();
            break;
         case COMMAND_IMPORT_LICENSE:
            returnCode = APIARImportLicense();
            break;
         case COMMAND_EXPORT_LICENSE:
            returnCode = APIARExportLicense();
            break;
         case COMMAND_GETLIST_SQL_FOR_AL:
            returnCode = APIARGetListSQLForActiveLink();
            break;
         case COMMAND_EXECUTE_PROCESS_FOR_AL:
            returnCode = APIARExecuteProcessForActiveLink();
            break;
         case COMMAND_DRIVER_VERSION:
            PrintDriverVersion();
            break;
         case COMMAND_GET_SESSION_CONFIGURATION:
            returnCode = APIARGetSessionConfiguration();
            break;
         case COMMAND_SET_SESSION_CONFIGURATION:
            returnCode = APIARSetSessionConfiguration();
            break;
         case COMMAND_ENCODE_DATE:
            APIARDateToJulianDate();
            break;
         case COMMAND_DECODE_DATE:
            APIARJulianDateToDate();
            break;
         case COMMAND_XML_CREATE_ENTRY:
            returnCode = APIARXMLCreateEntry();
            break;
         case COMMAND_XML_GET_ENTRY:
            returnCode = APIARXMLGetEntry();
            break;
         case COMMAND_XML_SET_ENTRY:
            returnCode = APIARXMLSetEntry();
            break;
         case COMMAND_GET_MULT_CURR_RATIO_SETS:
            returnCode = APIARGetMultipleCurrencyRatioSets();
            break;
         case COMMAND_GET_CURRENCY_RATIO:
            returnCode = APIARGetCurrencyRatio();
            break;
         case COMMAND_GET_MULTIPLE_ENTRYPOINTS:
            returnCode = APIARGetMultipleEntryPoints();
            break;
         case COMMAND_GETLIST_ROLE:
            returnCode = APIARGetListRole();
            break;
         case COMMAND_GETLIST_APPLICATION_STATES:
            returnCode = APIARGetListApplicationState();
            break;
         case COMMAND_GET_APPLICATION_STATE:
            returnCode = APIARGetApplicationState();
            break;
         case COMMAND_SET_APPLICATION_STATE:
            returnCode = APIARSetApplicationState();
            break;
         case COMMAND_BEGIN_BULK_ENTRY_TXN:
            returnCode = APIARBeginBulkEntryTransaction();
            break;
         case COMMAND_END_BULK_ENTRY_TXN:
            returnCode = APIAREndBulkEntryTransaction();
            break;
         case COMMAND_GETLIST_ENTRY_BLOCKS:
            returnCode = APIARGetListEntryBlocks();
            break;
         case COMMAND_SET_IMPERSONATED_USER:
            returnCode = APIARSetImpersonatedUser();
            break;
         case COMMAND_SERVICE_ENTRY:
            returnCode = APIARServiceEntry();
            break;
         case COMMAND_EXPORT_TO_FILE:
            returnCode = APIARExportToFile();
            break;
         case COMMAND_GET_CLIENT_CHARSET:
            returnCode = APIARGetClientCharSet();
            break;
         case COMMAND_GET_SERVER_CHARSET:
            returnCode = APIARGetServerCharSet();
            break;
         case COMMAND_XML_PARSE_DOCUMENT:
            returnCode = APIARParseXMLDocument();
            break;
         case COMMAND_CREATE_MULTIPLE_FIELDS:
            returnCode = APIARCreateMultipleFields();
            break;
         case COMMAND_SET_MULTIPLE_FIELDS:
            returnCode = APIARSetMultipleFields();
            break;
         case COMMAND_XML_SERVICE_ENTRY:
            returnCode = APIARXMLServiceEntry();
            break;
         case COMMAND_SET_SCH_FIELD_WITH_IMAGE:
            returnCode = APIARSetField(TRUE);
            break;
         case COMMAND_CREATE_SCH_FIELD_WITH_IMAGE:
            returnCode = APIARCreateField(TRUE);
            break;
         case COMMAND_GET_IMAGE:
            returnCode = APIARGetImage();
            break;
         case COMMAND_SET_IMAGE:
            returnCode = APIARSetImage();
            break;
         case COMMAND_CREATE_IMAGE:
            returnCode = APIARCreateImage();
            break;
         case COMMAND_DELETE_IMAGE:
            returnCode = APIARDeleteImage();
            break;
         case COMMAND_GETLIST_IMAGE:
            returnCode = APIARGetListImage();
            break;
         case COMMAND_GET_MULTIPLE_IMAGES:
            returnCode = APIARGetMultipleImages();
            break;
         case COMMAND_BEGIN_CLIENT_TRANSACTION:
            returnCode = APIARBeginClientManagedTransaction();
            break;
         case COMMAND_END_CLIENT_TRANSACTION:
            returnCode = APIAREndClientManagedTransaction();
            break;
        case COMMAND_SET_CLIENT_TRANSACTION:
            returnCode = APIARSetClientManagedTransaction();
            break;
         case COMMAND_REMOVE_CLIENT_TRANSACTION:
            returnCode = APIARRemoveClientManagedTransaction();
            break;
         case COMMAND_WFD_EXECUTE:
            returnCode = APIWFDExecute();
            break;
         case COMMAND_WFD_GET_CURRENT_LOCATION:
            returnCode = APIWFDGetStackLocation();
            break;
         case COMMAND_WFD_GET_FIELDVALUES:
            returnCode = APIWFDGetFieldValues();
            break;
         case COMMAND_WFD_SET_FIELDVALUES:
            returnCode = APIWFDSetFieldValues();
            break;
         case COMMAND_GET_OBJECT_CHANGE_TIMES:
            returnCode = APIARGetObjectChangeTimes();
            break;
         case COMMAND_WFD_GET_DEBUG_MODE:
            returnCode = APIWFDGetDebugMode();
            break;
         case COMMAND_WFD_SET_DEBUG_MODE:
            returnCode = APIWFDSetDebugMode();
            break;
         case COMMAND_WFD_GET_FILTER_QUAL:
            returnCode = APIWFDGetFilterQual();
            break;
         case COMMAND_WFD_SET_FILTER_QUAL:
            returnCode = APIWFDSetQualifierResult();
            break;
         case COMMAND_RUN_ESCALATION:
            returnCode = APIARRunEscalation();
            break;
         case COMMAND_WFD_TERMINATE:
            returnCode = APIWFDTerminateAPI();
            break;
         case COMMAND_WFD_GET_KEYWORD:
            returnCode = APIWFDGetKeyword();
            break;
         case COMMAND_GETLIST_ENTRY_WITH_MULTISCHEMA_FIELDS:
            returnCode = APIARGetListEntryWithMultiSchemaFields();
            break;
         case COMMAND_WFD_GET_USER_CONTEXT:
            returnCode = APIWFDGetUserContext();
            break;
         case COMMAND_WFD_RMT_SET_BKPT:
            returnCode = APIWFDRmtSetBreakPt();
            break;
         case COMMAND_WFD_RMT_CLR_BKPT:
            returnCode = APIWFDRmtClrBreakPt();
            break;
         case COMMAND_WFD_RMT_LIST_BKPT:
            returnCode = APIWFDRmtListBreakPt();
            break;
         case COMMAND_WFD_RMT_CLR_BP_LIST:
            returnCode = APIWFDRmtClearBpList();
            break;
         case COMMAND_GETONE_ENTRY_WITH_FIELDS:
            returnCode = APIARGetOneEntryWithFields();
            break;
         case COMMAND_GET_CACHE_EVENTS:
            returnCode = APIARGetCacheEvent();
            break;
         case COMMAND_SET_GET_ENTRY:
            returnCode = APIARSetGetEntry();
            break;
         case COMMAND_CREATE_OVERLAY:
            returnCode = APIARCreateOverlay();
            break;
         case COMMAND_CREATE_OVERLAY_FROM_OBJECT:
            returnCode = APIARCreateOverlayFromObject();
            break;
         case COMMAND_VERIFY_USER2:
            returnCode = APIARVerifyUser2();
            break;
         default:
            DriverPrintError(" **** No support for this command (%d) in driver \n",
                             commandCode);
            returnCode = AR_RETURN_ERROR;
            break;
      }
      if (returnCode > AR_RETURN_WARNING)
         gSavedExitCode = 1;

      if (gExitOnError && returnCode > AR_RETURN_WARNING)
         exit(gSavedExitCode);
   }
}


/*****************************************************************************/
/*                                                                           */
/*                     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)
#ifdef _WIN32
      return;
#else
      return NULL;
#endif

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

#ifndef _WIN32
   return NULL;
#endif
}


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

int 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 AR_RETURN_ERROR;
      }
   }

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

   /* open the input file for reading */

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

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

      if (snprintf(filename, AR_MAX_FULL_FILENAME, "%s%s%s", gResultDir,
                   (gResultDir[0] != '\0') ? "\\" : "", inputValue) < 0)
         filename[AR_MAX_FULL_FILENAME] = '\0';

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

      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 AR_RETURN_ERROR;
      }

      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 AR_RETURN_ERROR;
      }

      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 AR_RETURN_ERROR;
      }
      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 AR_RETURN_ERROR;
      }

      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 AR_RETURN_ERROR;
      }

      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 AR_RETURN_ERROR;
      }

      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 AR_RETURN_ERROR;
      }

      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 AR_RETURN_ERROR;
   }

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

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

   return AR_RETURN_OK;
}


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