/* dbstress: A program to stress test the database engine 'Bbuuzzb'.
   Program picks a random number which represents a command type and
   then executes that command. Program is an infinite loop and will
   have to be killed to terminate. Command numbers are taken from
   'dbcomm.h'. Rick Smereka, Copyright (C) 1997-2006.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, get a copy via the Internet at
   http://gnu.org/copyleft/gpl.html or write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
   MA 02111-1307 USA

   You can contact the author via email at rsmereka@future-lab.com

   Original DOS version Nov/97, Rick Smereka

   Added transaction number, program logo to log file and client/server
   version. Ported to QNX V4.23a Dec/97, Rick Smereka

   Added support for the table 'is_table_locked' flag. Jan/98,
   Rick Smereka

   Added #include's for 'dblocal' and 'dbiocode' as they
   were missing. Nov/98, Rick Smereka

   Ported to 32bit Windows under CodeWarrior V4.
   Dec/98, Rick Smereka

   Ported to HP-UX under GNU C 2.8.1.
   Jan/99, Rick Smereka

   Ported to Red Hat Linux 5.2, Jul/99, Rick Smereka

   Updated to include new (mostly configuration) commands. Program will
   not process all commands as some are just too dangerous. The following
   commands will be ignored:

      DBCOM_SET_POS               set file position
      DBCOM_CONFIG_SET_TMP_PATH   set config path for temporary files
      DBCOM_CONFIG_SET_LOG        set config error log file
      DBCOM_CONFIG_SET_SESSION    set config session table (multi only)
      DBCOM_CONFIG_SET_CATALOG    set config catalog table (multi only)

   When these commands are chosen, an 'unknown command' message will
   appear in the output log. Aug/99, Rick Smereka
   
   Re-compiled after re-structuring the definition of the common
   send and repy codes. Moved 'include' of the header 'flsocket.h'
   inside the 'MULTIUSER' if statement. Modified to use the
   'socloc' API if in multiuser mode. Changed 'c_config_set_log_flag'
   to turn logging back on if it was turned off. Added command line
   parameters to specify the primary and alternate table names as well
   as the log file name. Program syntax is now:
   
   dbstress|dbstresm [-p primary_table] [-a alternate_table] [-l log_file]
   
   Where '-p primary_table' is the optional name of the Bbuuzzb primary
   table to use, '-a alternate_table' is the alternate table and
   '-l log_file' is the log file to use. Any of these three items
   will default to their defined values if not present on the command
   line. Note that the switch character ('-') is based on the platform
   (see include file 'stdhead.h'). Apr/2000, Rick Smereka
   
   Re-compiled after adding the new 'clib' module 'ip.c'.
   Jul/2000, Rick Smereka

   Added support for the new command 'get_open_table_list'.
   Re-compiled for new database engine version. Jun/2001,
   Rick Smereka

   Modified for use with both sockets and QNX message passing IPC.
   Oct/2001, Rick Smereka

   Removed support for the following commands:

      DBCOM_CONFIG_SET_LOG_FLAG
      DBCOM_CONFIG_SET_SESSION_FLAG
      DBCOM_CONFIG_SET_CATALOG_FLAG

   It is assumed that these will be set on the database server prior
   to running this program and the values will not be changed during
   the course of this program run.

   Modified to use system catalog in the client/sever version.
   The default table names have been changed to the logical names:

      test.primary
      test.alternate

   When supplying table names on the command line, use logical names
   if the server catalog is operational. Nov/2001, Rick Smereka
 
   Added support of the new database commands 'DBCOM_GET_NSUBFIELDS',
   'DBCOM_GET_NSUBFIELD_SIZE', 'DBCOM_GET_SUBFIELD', 'DBCOM_PUT_SUBFIELD',
   'DBCOM_GET_NSUBSUBFIELDS', 'DBCOM_GET_NSUBSUBFIELD_SIZE',
   'DBCOM_GET_SUBSUBFIELD', 'DBCOM_PUT_SUBSUBFIELD',
   'DBCOM_CONFIG_GET_REPLICATE_FLAG'. Excluded
   'DBCOM_CONFIG_SET_REPLICATE_FLAG'. This should be set on the
   database server prior to executing this program. Added log
   output of current record position before command is executed.
   Jan/2002, Rick Smereka

   Excluded the include of 'flsocket.h' in the case of DOS.
   Apr/2002, Rick Smereka

   Added support for automatic field, subfield and subsubfield
   append using the value zero. Functions 'c_put_field',
   'c_put_subfield' and 'c_put_subsubfield' now generate zero
   values (in the field, subfield and subsubfield number parameter)
   to signify an append. May/2002, Rick Smereka

   Added support for the commands 'DBCOM_EXIST', 'DBCOM_GET_CATALOG_LIST',
   'DBCOM_SORT', 'DBCOM_GET_SORT_MEM', 'DBCOM_SET_SORT_MEM',
   'DBCOM_GET_SORT_OPEN_BIN' and 'DBCOM_SET_SORT_OPEN_BIN'. The
   commmands 'DBCOM_REPLICATE_UPDATE', 'DBCOM_DELETE_TABLE', 
   'DBCOM_CLEAR_TABLE' and 'DBCOM_COPY_TABLE' will not be processed.
   Dec/2002, Rick Smereka

   Ported to Debian Linux. Jan/2003, Rick Smereka

   Added support for the commands 'DBCOM_DELETE_FIELD',
   'DBCOM_DELETE_SUBFIELD' and 'DBCOM_DELETE_SUBSUBFIELD'. Mar/2003,
   Rick Smereka

   Modified for the updated 'socloc' API ('sloc.c').
   May/2003, Rick Smereka

   Modified for the updated 'sys_log' API ('sys_log.c').
   Modified for the updated 'bbuuzzb' API.
   Added support for the commands 'DBCOM_TRANS_NUM' and
   'DBCOM_CONNECT_NUM'. Jun/2003, Rick Smereka

   Modified default table names to use only logical table names
   for both the single user version and the client/server
   version. This will require that the database catalog be
   active (see 'dbengcfg.c') in all modes. Changed calls to
   'db_count' and 'db_get_rec_count' to use new parameters.
   Feb/2004, Rick Smereka

   Added support for the 'DBCOM_GET_AUTOPACK' and
   'DBCOM_SET_AUTOPACK' commmands. Mar/2004,
   Rick Smereka

   Added support for the commands 'DBCOM_CONFIG_CLIENT_GET_LOG' and
   'DBCOM_CONFIG_CLIENT_GET_LOG_FLAG'. The commmands
   'DBCOM_CONFIG_CLIENT_SET_LOG' and 'DBCOM_CONFIG_CLIENT_SET_LOG_FLAG'
   have not been provided for as the client log file (client/server
   only) should not be changed during the run. The application does
   not yet use the log manager ('logman') because once the log is
   turned on, the application and all the API's would start logging
   all messages. May/2004, Rick Smereka

   Added include of 'appinit.h' and added calls to register and
   de-register application name. Dec/2004, Rick Smereka

   Re-compile after changing the 'socloc' API.
   Jan/2005, Rick Smereka

   Re-compile after modifications to low level TCP socket communication
   module (ipcomm.c). Feb/2006, Rick Smereka */

#include "stdhead.h"
#ifndef OS_DOS
#include "flsocket.h"
#include "appinit.h"
#endif
#include "dbmess.h"
#include "dbcomm.h"
#include "dbiocode.h"

#ifdef IPC_TCP
#include "socloc.h"
#include "sloc.h"
#include "slconfig.h"
#include "sliocode.h"
#ifdef OS_WIN32
WSADATA wsaData;
#endif
#endif

#ifdef MULTIUSER
#include "dbcs.h"
#include "dbcscfg.h"
#else
#include "dbeng.h"
#include "dblocal.h"
#include "dbengcfg.h"
#include "dblocfg.h"
#endif

#define VERSION "1.49.02-2006.02.26"
// #define DEBUG 1
// #define NUM_RECS 20000

#ifdef MULTIUSER
#define APNAME "dbstresm"
#else
#define APNAME "dbstress"
#endif

// default logical table names

#define DS_PRIM "test.primary"
#define DS_ALT "test.alternate"

// default log file names that depend on platform

#ifdef OS_UNIX
#define DS_LOG "/work/logs/dbstres"
#endif

#ifdef OS_WIN32
#define DS_LOG "C:\\LOGS\\DBSTRES"
#endif

#ifdef OS_DOS
#define DS_LOG "C:\\LOGS\\DBSTRES"
#endif

/* function prototypes */

int main(int, char **);
int parse_comline(int, char **);
int c_initialize(void);
int check_files(void);
int pick_com(void);
void do_com(int);
void c_open(void);
void c_close(void);
void c_next(void);
void c_top(void);
void c_get_rec(void);
void c_get_field(void);
void c_get_subfield(void);
void c_get_subsubfield(void);
void c_get_rec_size(void);
void c_get_field_size(void);
void c_get_subfield_size(void);
void c_get_subsubfield_size(void);
void c_goto(void);
void c_count(void);
void c_put_field(void);
void c_put_subfield(void);
void c_put_subsubfield(void);
void c_write(void);
void c_delete(void);
void c_get_delete_flag(void);
void c_set_delete_flag(void);
void c_get_nfields(void);
void c_get_nsubfields(void);
void c_get_nsubsubfields(void);
void c_get_rec_num(void);
void c_get_pos(void);
void c_get_change_rec_flag(void);
void c_set_change_rec_flag(void);
void c_pack(void);
void c_new(void);
void c_find(void);
void c_find_field(void);
void c_find_part(void);
void c_find_field_part(void);
void c_get_rec_count(void);
void c_get_enf_change_rec_flag(void);
void c_set_enf_change_rec_flag(void);
void c_get_is_table_locked(void);
void c_set_is_table_locked(void);
void c_new_table(void);
void c_config_get_tmp_path(void);
void c_config_get_log(void);
void c_config_get_session(void);
void c_config_get_catalog(void);
void c_config_get_log_flag(void);
void c_config_get_session_flag(void);
void c_config_get_catalog_flag(void);
void c_config_get_replicate_flag(void);
void c_config_get_version(void);
void c_get_open_table_list(void);
void c_version(void);
void c_exist(void);
void c_get_catalog_list(void);
void c_sort(void);
void c_get_sort_mem(void);
void c_set_sort_mem(void);
void c_get_sort_open_bin(void);
void c_set_sort_open_bin(void);
void c_delete_field(void);
void c_delete_subfield(void);
void c_delete_subsubfield(void);
void c_trans_num(void);
void c_connect_num(void);
void c_get_autopack(void);
void c_set_autopack(void);
void c_config_client_get_log(void);
void c_config_client_get_log_flag(void);
void c_put_data(void);
void term_app(void);

#ifdef IPC_TCP
int c_ipc_socloc_init(void);
#endif

/* global data */

char *data;
char ds_log[128], ds_prim[128], ds_alt[128];
int tid;
long trans_no;

int main(int argc, char **argv)
{
   char mes[128];
   long now;   
   int dbcom;
   int ret;

   strcpy(ds_prim, DS_PRIM);
   strcpy(ds_alt, DS_ALT);

   // load log name
      
#ifndef MULTIUSER
   sprintf(ds_log, "%ss.log", DS_LOG);
#else
   sprintf(ds_log, "%sm.log", DS_LOG);
#endif

   /* parse command line (if parameters supplied) */

   if (argc > 1)   
      if (!parse_comline(argc, argv))
         return(0);

#ifndef OS_DOS
   // register application name with 'appinit'

   if (!appinit_register_name(APNAME))
      {
      printf("%s:fatal error registering app name with appinit\n", APNAME);
      return(0);
      }
#endif

   if (!log_start(ds_log))
      {
      printf("%s:unable to open log file, abort\n", APNAME);
      return(0);
      }      

   log_console(TRUE);
   srand(time(NULL));
   // srand(time(&now) % 37);                /* seed rnd gen */
   trans_no = 1L;

   /* build logo string based on single or multi user and platform */

#ifndef MULTIUSER
#ifndef OS_UNIX
   /* single user non-Unix */

   log_file_date("dbstress Stand-Alone for %s Version %s", PLATFORM_STRING,
           VERSION);
#else
   /* single user Unix */

   log_file_date("dbstress Stand-Alone for %s Version %s", SUB_PLATFORM_STRING,
           VERSION);
#endif
#else
#ifndef OS_UNIX
   /* c/s non-Unix */

   log_file_date("dbstresm Client/Server for %s Version %s", PLATFORM_STRING,
           VERSION);
#else
   /* c/s Unix */

   log_file_date("dbstresm Client/Server for %s Version %s", SUB_PLATFORM_STRING,
           VERSION);
#endif
#endif

   log_file_date_nf("By Rick Smereka, Copyright (c) 1997-2006");

   /* initialize local engine if we are running locally or
      startup the 'socloc' interface (TCP only) and connect to a
      Bbuuzzb database server if we are running in
      client/server mode */

   if (!c_initialize())
      {
      term_app();
      return(0);
      }

   /* initialize for system logging, used only for debugging */

#ifdef DEBUG
#ifndef OS_DOS
   /* if ((ret = sys_log_init(APNAME)) != SYS_LOG_OK)
      {
      sys_log_code_string(ret, mes);
      printf("%s:bad rc[%s] from sys_log_init, program abort\n",
             APNAME, mes);
      term_app();
      return(0);
      } */

#ifdef MULTIUSER
   if (logman_start("/work/logs/dbeng.log", APNAME))
      {
      printf("%s:unable to start log\n", APNAME);
      term_app();
      return(0);
      }
#else      
   if ((ret = db_config_set_log_flag(TRUE)) != DBENG_OK)
      {
      printf("%s:unable to start log\n", APNAME);
      term_app();
      return(0);
      }
#endif
   logman_console(TRUE);
#endif
#endif

   /* get and display 'dbeng' version */

   if ((ret = db_config_get_version(mes)) != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      printf("%s:error getting 'dbeng' version,rc=%s\n", APNAME, mes);
      term_app();
      return(0);
      }

   log_file_date("based on version %s of the 'Bbuuzzb' database engine", mes);
   log_file_date("%s comes with ABSOLUTELY NO WARRANTY", APNAME);
   log_file_date_nf("This is free software, and you are welcome to redistribute it");
   log_file_date_nf("under certain conditions; see 'gpl.txt' for information.");

   /* allocate storage for record data, 32000 bytes should be enough */

   if ((data = (char *)malloc(32000)) == NULL)
      {
      printf("alloc fail 'data', program abort\n");
      term_app();
      return(0);
      }

   /* check primary and alternate table names */

   if (!check_files())
      {
      free(data);
      term_app();
      return(0);
      }

   /* display primary table, alternate table and log file names */
   
   log_file_date("%s:primary table is %s,tid=%d", APNAME, ds_prim, tid);
   log_file_date("%s:alternate table is %s", APNAME, ds_alt);
   log_file_date("%s:log file is %s", APNAME, ds_log);

   /* main loop, infinite */

   while(1)
      {
      dbcom = pick_com();
      trans_no++;
      do_com(dbcom);
      }

   free(data);
   term_app();
   return(0);
}

int parse_comline(int c_count, char **c_parm)
{
   /* Parse the command line for parameters. Function
      returns 'TRUE' if no error was detected, 'FALSE'
      otherwise. */
      
   int parms = 1, done = FALSE;
      
   while(!done)
      {
      if (c_parm[parms][0] == SWITCH_CHAR)
         {
         switch(c_parm[parms][1])
            {
            case 'l':
            case 'L':                        
               if (strlen(c_parm[parms]) > 2)
                  printf("%s:extraneous input with log file switch, "
                         "ignoring\n", APNAME);
                         
               parms++;
            
               if (parms >= c_count)
                  {
                  printf("%s:log file switch with no file name, "
                         "program abort\n", APNAME);
                  return(FALSE);
                  }
               
               strcpy(ds_log, c_parm[parms]);
               parms++;
               break;
         
            case 'p':
            case 'P':
               if (strlen(c_parm[parms]) > 2)
                  printf("%s:extraneous input with primary table switch, "
                         "ignoring\n", APNAME);
               
               parms++;
            
               if (parms >= c_count)
                  {
                  printf("%s:primary table switch with no file name, "
                         "program abort\n", APNAME);
                  return(FALSE);
                  }
               
               strcpy(ds_prim, c_parm[parms]);
               parms++;
               break;

            case 'a':
            case 'A':
               if (strlen(c_parm[parms]) > 2)
                  printf("%s:extraneous input with alternate table switch, "
                         "ignoring\n", APNAME);
               
               parms++;
            
               if (parms >= c_count)
                  {
                  printf("%s:alternate table switch with no file name, "
                         "program abort\n", APNAME);
                  return(FALSE);
                  }
               
               strcpy(ds_alt, c_parm[parms]);
               parms++;
               break;

            default:
               printf("%s:unknown switch[%s], program abort\n", APNAME,
                      c_parm[parms]);
               return(FALSE);
            };
         }
      else
         {
         printf("%s:unknown parameter[%s], program abort\n", APNAME,
                c_parm[parms]);
         return(FALSE);
         }
         
      if (parms >= c_count)
         done = TRUE;
      }

   return(TRUE);
}

#ifdef MULTIUSER
int c_initialize(void)
{
   /* Initialize socket communication, form connection to
      'socloc' server, update the config list and connect
      to a 'Bbuuzzb' server. Function returns 'TRUE' upon
      success, 'FALSE' otherwise. */

   char mname[] = "c_initialize";
   char mes[128];
   int ret;

#ifdef IPC_TCP
   if (!c_ipc_socloc_init())
      return(FALSE);
#endif

   /* attempt to locate a 'Bbuuzzb' server and connect to it */
   
   if ((ret = db_initialize()) != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("%s:error connecting to the Bbuuzzb server,rc[%s]. "
                    "Program abort", mname, mes);
      return(FALSE);
      }
        
   /* display current connected server host name and
      TCP port number */
      
#ifdef IPC_TCP
   (void)db_get_active(mes, &ret);
   log_file_date("%s:connected to Bbuuzzb server %s on TCP "
                 "port %d (decimal)", mname, mes, ret);    
#endif
   return(TRUE);
}
#endif

#ifndef MULTIUSER
int c_initialize(void)
{
   /* Start the 'DBENG' local engine, read the config file
      and display the result settings. Function returns
      'TRUE' upon success, 'FALSE' otherwise. */

   char mname[] = "c_initialize";
   char *mes;
   int ret, flag;

#ifdef DEBUG
#ifdef IPC_TCP
   if (!c_ipc_socloc_init())
      return(FALSE);
#endif
#endif

   if ((mes = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
      {
      log_file_date("%s:alloc fail[mes]. Program abort", mname);
      return(FALSE);
      }

   ret = db_initialize();

   /* interpret code from init which may be
      a fatal config error */

   switch(ret)
      {
      case DBENG_OK:
         log_file_date("%s:read config file successfully", mname);
         break;

      case DBENG_CONFIG_UNABLE_TO_OPEN:
         log_file_date("%s:config file not found, loaded defaults", mname);
         break;

      default:
         db_io_code_string(ret, mes);
         log_file_date("%s:fatal config error[%s]", mname, mes);
         log_file_date("%s:terminating", mname);
         free(mes);
         return(FALSE);
         break;
      };

   (void)db_config_get_tmp_path(mes);
   log_file_date("%s:tmp path is '%s'", mname, mes);
   (void)db_config_get_log(mes);
   log_file_date("%s:log is '%s'", mname, mes);

   if ((ret = db_config_get_catalog(mes)) == DBENG_OK)
      printf("%s:catalog is '%s'\n", mname, mes);
   else
      printf("%s:catalog name not loaded\n", mname);

   (void)db_config_get_catalog_flag(&flag);

   if (flag_2_logic_string(flag, mes))
      printf("%s:catalog is %s\n", mname, mes);

   free(mes);      
   return(TRUE);
}
#endif

#ifdef IPC_TCP
int c_ipc_socloc_init(void)
{
   /* Initialize socket communication, form connection to
      'socloc' server (TCP only) and update the config list.
      Function returns 'TRUE' upon success, 'FALSE' otherwise. */

   char mname[] = "c_ipc_socloc_init";
   char *thelist, mes[128];
   int ret;

   /* startup WinSock (if Windoze) */

#ifdef OS_WIN32
   if (WSAStartup(WINSOCK_VERSION, &wsaData))
      {
      printf("%s:unable to start WinSock. "
            "Program abort\n", mname);
      return(FALSE);
      }
#endif

   /* attempt to connect to a 'socloc' server */

   if ((ret = sloc_initialize()) != SL_OK)
      {
      sl_code_string(ret, mes);
      printf("%s:error initializing socloc interface,rc[%s]. "
            "Program abort\n", mname, mes);
      return(FALSE);
      }

   /* get config list from 'socloc' server and update
      the client list with it */

   if ((thelist = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      printf("%s:alloc fail[thelist]. Program abort\n", mname);
      return(FALSE);
      }

   if ((ret = sloc_config_get_list(thelist)) != SL_OK)
      {
      free(thelist);
      sl_code_string(ret, mes);
      printf("%s:error getting socloc config list,rc[%s]. "
             "Program abort\n", mname, mes);
      return(FALSE);
      }

   if ((ret = sl_config_put_list(thelist)) != SL_OK)
      {
      sl_code_string(ret, mes);
      printf("%s:error putting socloc config list,rc[%s]. ",
             "Program abort\n", mname, mes);
      free(thelist);
      return(FALSE);
      }

   return(TRUE);
}
#endif

int check_files(void)
{
   /* Check the resolved primary table and alternate table.
      Function returns 'TRUE' if files are valid, 'FALSE'
      otherwise. */

   char mes[128];
   int a_tid;
   int rc;
         
   /* open primary test table (this will stay open)
      as long as their is no error */

   if ((rc = db_open(ds_prim, &tid)) != DBENG_OK)
      {
      db_io_code_string(rc, mes);
      log_file_date("%s:primary table[%s] open failed(rc[%d]=%s), abort",
                    APNAME, ds_prim, rc, mes);
      return(FALSE);
      }
      
   /* check alternate table */
   
   if ((rc = db_open(ds_alt, &a_tid)) != DBENG_OK)
      {
      db_io_code_string(rc, mes);
      log_file_date("%s:alternate table[%s] open failed(rc[%d]=%s), abort",
                    APNAME, ds_alt, rc, mes);
      (void)db_close(tid);
      return(FALSE);
      }
   
   (void)db_close(a_tid);
   return(TRUE);
}   
           
int pick_com(void)
{
   /* Get a random command number in range. */

   long rec_num, pos, active_rec_count, deleted_rec_count;
   int rnd_num;
   int final;

   if ((trans_no % 500L) == 0L)
      srand(time(NULL));

   final = rnd(1, DBCOM_MAXCOM);
   (void)db_get_rec_num(tid, &rec_num);
   (void)db_get_pos(tid, &pos);
   (void)db_get_rec_count(tid, &active_rec_count, &deleted_rec_count);
   log_file_date("pick_com:c=%d,arc=%ld,drc=%ld,rn=%ld,pos=%ld,t=%ld", 
                 final, active_rec_count, deleted_rec_count, rec_num, 
                 pos, trans_no);
   return(final);
}

void do_com(int com_num)
{
   /* Dispatch command. */

   switch(com_num)
      {
      case DBCOM_OPEN:
         c_open();
         break;

      case DBCOM_CLOSE:
         c_close();
         break;

      case DBCOM_NEXT:
         c_next();
         break;

      case DBCOM_TOP:
         c_top();
         break;

      case DBCOM_GET_REC:
         c_get_rec();
         break;

      case DBCOM_GET_FIELD:
         c_get_field();
         break;

      case DBCOM_GOTO:
         c_goto();
         break;

      case DBCOM_COUNT:
         c_count();
         break;

      case DBCOM_PUT_FIELD:
         c_put_field();
         break;

      case DBCOM_WRITE:
         c_write();
         break;

      case DBCOM_DELETE:
         c_delete();
         break;

      case DBCOM_GET_DELETE_FLAG:
         c_get_delete_flag();
         break;

      case DBCOM_SET_DELETE_FLAG:
         c_set_delete_flag();
         break;

      case DBCOM_GET_NFIELDS:
         c_get_nfields();
         break;

      case DBCOM_GET_REC_SIZE:
         c_get_rec_size();
         break;

      case DBCOM_GET_FIELD_SIZE:
         c_get_field_size();
         break;

      case DBCOM_GET_REC_NUM:
         c_get_rec_num();
         break;

      case DBCOM_GET_POS:
         c_get_pos();
         break;

      case DBCOM_GET_CHANGE_REC_FLAG:
         c_get_change_rec_flag();
         break;

      case DBCOM_SET_CHANGE_REC_FLAG:
         c_set_change_rec_flag();
         break;

      case DBCOM_PACK:
         c_pack();
         break;

      case DBCOM_NEW:
         c_new();
         break;

      case DBCOM_FIND:
         c_find();
         break;

      case DBCOM_FIND_FIELD:
         c_find_field();
         break;

      case DBCOM_FIND_PART:
         c_find_part();
         break;

      case DBCOM_FIND_FIELD_PART:
         c_find_field_part();
         break;

      case DBCOM_GET_REC_COUNT:
         c_get_rec_count();
         break;

      case DBCOM_GET_ENF_CHANGE_REC_FLAG:
         c_get_enf_change_rec_flag();
         break;

      case DBCOM_SET_ENF_CHANGE_REC_FLAG:
         c_set_enf_change_rec_flag();
         break;

      case DBCOM_GET_IS_TABLE_LOCKED:
         c_get_is_table_locked();
         break;

      case DBCOM_SET_IS_TABLE_LOCKED:
         c_set_is_table_locked();
         break;

      case DBCOM_NEW_TABLE:
         c_new_table();
         break;

      case DBCOM_CONFIG_GET_TMP_PATH:
         c_config_get_tmp_path();
         break;

      case DBCOM_CONFIG_GET_LOG:
         c_config_get_log();
         break;

      case DBCOM_CONFIG_GET_SESSION:
         c_config_get_session();
         break;

      case DBCOM_CONFIG_GET_CATALOG:
         c_config_get_catalog();
         break;

      case DBCOM_CONFIG_GET_LOG_FLAG:
         c_config_get_log_flag();
         break;

      case DBCOM_CONFIG_GET_SESSION_FLAG:
         c_config_get_session_flag();
         break;

      case DBCOM_CONFIG_GET_CATALOG_FLAG:
         c_config_get_catalog_flag();
         break;

      case DBCOM_CONFIG_GET_REPLICATE_FLAG:
         c_config_get_replicate_flag();
         break;

      case DBCOM_CONFIG_GET_VERSION:
         c_config_get_version();
         break;

      case DBCOM_GET_OPEN_TABLE_LIST:
         c_get_open_table_list();
         break;

      case DBCOM_GET_NSUBFIELDS:
         c_get_nsubfields();
         break;

      case DBCOM_GET_NSUBSUBFIELDS:
         c_get_nsubsubfields();
         break;

      case DBCOM_GET_SUBFIELD_SIZE:
         c_get_subfield_size();
         break;

      case DBCOM_GET_SUBSUBFIELD_SIZE:
         c_get_subsubfield_size();
         break;

      case DBCOM_GET_SUBFIELD:
         c_get_subfield();
         break;

      case DBCOM_GET_SUBSUBFIELD:
         c_get_subsubfield();
         break;

      case DBCOM_PUT_SUBFIELD:
         c_put_subfield();
         break;

      case DBCOM_PUT_SUBSUBFIELD:
         c_put_subsubfield();
         break;

      case DBCOM_VERSION:
         c_version();
         break;
         
      case DBCOM_EXIST:
         c_exist();
         break;

      case DBCOM_GET_CATALOG_LIST:
         c_get_catalog_list();
         break;

      case DBCOM_SORT:
         c_sort();
         break;

      case DBCOM_GET_SORT_MEM:
         c_get_sort_mem();
         break;

      case DBCOM_SET_SORT_MEM:
         c_set_sort_mem();
         break;

      case DBCOM_GET_SORT_OPEN_BIN:
         c_get_sort_open_bin();
         break;

      case DBCOM_SET_SORT_OPEN_BIN:
         c_set_sort_open_bin();
         break;

      case DBCOM_DELETE_FIELD:
         c_delete_field();
         break;

      case DBCOM_DELETE_SUBFIELD:
         c_delete_subfield();
         break;

      case DBCOM_DELETE_SUBSUBFIELD:
         c_delete_subsubfield();
         break;

      case DBCOM_TRANS_NUM:
         c_trans_num();
         break;

      case DBCOM_CONNECT_NUM:
         c_connect_num();
         break;

      case DBCOM_GET_AUTOPACK:
         c_get_autopack();
         break;

      case DBCOM_SET_AUTOPACK:
         c_set_autopack();
         break;

      case DBCOM_CONFIG_CLIENT_GET_LOG:
         c_config_client_get_log();
         break;

      case DBCOM_CONFIG_CLIENT_GET_LOG_FLAG:
         c_config_client_get_log_flag();
         break;

      default:
         log_file_date("do_com:unknown com[%d], ignoring", com_num);
      };
}

void c_open(void)
{
   /* Test 'open' function by opening and then closing a secondary table. */

   char mes[50];
   int ret;
   int atid;

   ret = db_open(ds_alt, &atid);
   db_io_code_string(ret, mes);
   log_file_date("c_open:open,rc=%s", mes);
   ret = db_close(atid);
   db_io_code_string(ret, mes);
   log_file_date("c_open:close,t=%d,rc=%s", atid, mes);
}

void c_close(void)
{
   /* Test 'close' function by opening and then closing a secondary table. */

   char mes[50];
   int ret;
   int atid;

   ret = db_open(ds_alt, &atid);
   db_io_code_string(ret, mes);
   log_file_date("c_close:open,rc=%s", mes);
   ret = db_close(atid);
   db_io_code_string(ret, mes);
   log_file_date("c_close:close,t=%d,rc=%s", atid, mes);
}

void c_next(void)
{
   /* Test 'next' function by going to next record. If 'eof' is
      detected, go to the top of the table and get first record. */

   char mes[50];
   int ret;

   ret = db_next(tid);

   if (ret == DBENG_EOF)
      {
      log_file_date_nf("c_next:eof detected, getting first");
      (void)db_top(tid);
      ret = db_next(tid);
      }

   db_io_code_string(ret, mes);
   log_file_date("c_next:rc=%s", mes);
}

void c_top(void)
{
   /* Test 'top' function by going to top of the table and getting the
      first record. */

   char mes[50];
   int ret;

   ret = db_top(tid);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_top:bad rc=%s", mes);
      return;
      }

   ret = db_next(tid);
   db_io_code_string(ret, mes);
   log_file_date("c_top:rc=%s", mes);
}

void c_get_rec(void)
{
   /* Test 'get_rec' function by getting the contents of the current
      record. */

   char mes[50];
   int ret;

   ret = db_get_rec(tid, data);
   db_io_code_string(ret, mes);
   log_file_date("c_get_rec:l=%d,rc=%s", strlen(data), mes);
}

void c_get_field(void)
{
   /* Test 'get_field' function by getting the contents of a
      randomly selected field. */

   char mes[50];
   int ret;
   int nfields;
   int which;

   ret = db_get_nfields(tid, &nfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_field:nfields,rc=%s", mes);
      return;
      }

   which = (nfields > 1) ? rnd(1, nfields) : 1;
   ret = db_get_field(tid, which, data);
   db_io_code_string(ret, mes);
   log_file_date("c_get_field:f=%d,d=%s,l=%d,rc=%s", which, data,
                 strlen(data), mes);
}

void c_get_subfield(void)
{
   /* Test 'get.subfield' function. */

   char mes[50];
   int ret;
   int nfields, nsubfields;
   int which_field, which_subfield;

   ret = db_get_nfields(tid, &nfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_subfield:nfields,rc=%s", mes);
      return;
      }

   which_field = (nfields > 1) ? rnd(1, nfields) : 1;
   ret = db_get_nsubfields(tid, which_field, &nsubfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_subfield:nsubfields,rc=%s", mes);
      return;
      }

   which_subfield = (nsubfields > 1) ? rnd(1, nsubfields) : 1;
   ret = db_get_subfield(tid, which_field, which_subfield, data);
   db_io_code_string(ret, mes);
   log_file_date("c_get_subfield:f=%d,sf=%d,d=%s,l=%d,rc=%s", which_field,
                 which_subfield, data, strlen(data), mes);
}

void c_get_subsubfield(void)
{
   /* Test 'get.subsubfield' function. */

   char mes[50];
   int ret;
   int nfields, nsubfields, nsubsubfields;
   int which_field, which_subfield, which_subsubfield;

   ret = db_get_nfields(tid, &nfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_subsubfield:nfields,rc=%s", mes);
      return;
      }

   which_field = (nfields > 1) ? rnd(1, nfields) : 1;
   ret = db_get_nsubfields(tid, which_field, &nsubfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_subsubfield:nsubfields,rc=%s", mes);
      return;
      }

   which_subfield = (nsubfields > 1) ? rnd(1, nsubfields) : 1;
   ret = db_get_nsubsubfields(tid, which_field, which_subfield, 
                              &nsubsubfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_subsubfield:nsubsubfields,rc=%s", mes);
      return;
      }

   which_subsubfield = (nsubsubfields > 1) ? rnd(1, nsubsubfields) : 1;
   ret = db_get_subsubfield(tid, which_field, which_subfield, 
                            which_subsubfield, data);
   db_io_code_string(ret, mes);
   log_file_date("c_get_subsubfield:f=%d,sf=%d,ssf=%d,d=%s,l=%d,rc=%s", 
                 which_field, which_subfield, which_subsubfield,
                 data, strlen(data), mes);
}

void c_get_rec_size(void)
{
   /* Test 'get_rec_size' function by getting the size of the
      current record. */

   char mes[50];
   int ret;
   int rsize;

   ret = db_get_rec_size(tid, &rsize);
   db_io_code_string(ret, mes);
   log_file_date("c_get_rec_size:s=%d,rc=%s", rsize, mes);
}

void c_get_field_size(void)
{
   /* Test 'get_field_size' function by getting the size of a
      a randomly selected field. */

   char mes[50];
   int ret;
   int nfields;
   int which;
   int fsize;

   ret = db_get_nfields(tid, &nfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_field:nfields,rc=%s", mes);
      return;
      }

   which = (nfields > 1) ? rnd(1, nfields) : 1;
   ret = db_get_field_size(tid, which, &fsize);
   db_io_code_string(ret, mes);
   log_file_date("c_get_field_size:f=%d,l=%d,rc=%s", which, fsize, mes);
}

void c_get_subfield_size(void)
{
   /* Test 'get.subfield.size' function. */

   char mes[50];
   int ret;
   int nfields, nsubfields, size;
   int which_field, which_subfield;

   ret = db_get_nfields(tid, &nfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_subfield_size:nfields,rc=%s", mes);
      return;
      }

   which_field = (nfields > 1) ? rnd(1, nfields) : 1;
   ret = db_get_nsubfields(tid, which_field, &nsubfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_subfield_size:nsubfields,rc=%s", mes);
      return;
      }

   which_subfield = (nsubfields > 1) ? rnd(1, nsubfields) : 1;
   ret = db_get_subfield_size(tid, which_field, which_subfield, &size);
   db_io_code_string(ret, mes);
   log_file_date("c_get_subfield_size:f=%d,sf=%d,size=%d,rc=%s", which_field,
                 which_subfield, size, mes);
}

void c_get_subsubfield_size(void)
{
   /* Test 'get.subsubfield.size' function. */

   char mes[50];
   int ret;
   int nfields, nsubfields, nsubsubfields, size;
   int which_field, which_subfield, which_subsubfield;

   ret = db_get_nfields(tid, &nfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_subsubfield_size:nfields,rc=%s", mes);
      return;
      }

   which_field = (nfields > 1) ? rnd(1, nfields) : 1;
   ret = db_get_nsubfields(tid, which_field, &nsubfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_subsubfield_size:nsubfields,rc=%s", mes);
      return;
      }

   which_subfield = (nsubfields > 1) ? rnd(1, nsubfields) : 1;
   ret = db_get_nsubsubfields(tid, which_field, which_subfield, &nsubsubfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_subsubfield_size:nsubsubfields,rc=%s", mes);
      return;
      }

   which_subsubfield = (nsubsubfields > 1) ? rnd(1, nsubsubfields) : 1;
   ret = db_get_subsubfield_size(tid, which_field, which_subfield, 
                                 which_subsubfield, &size);
   db_io_code_string(ret, mes);
   log_file_date("c_get_subsubfield_size:f=%d,sf=%d,ssf=%d,size=%d,rc=%s", 
                 which_field, which_subfield, which_subsubfield, size, mes);
}

void c_goto(void)
{
   /* Test 'goto' function by going to a randomly selected record. */

   char mes[50];
   long active_rec_count, deleted_rec_count;
   int ret;
   long which;

   /* pick a random number zero to rec count + 10 */

   (void)db_get_rec_count(tid, &active_rec_count, &deleted_rec_count);
   which = (long)rnd(0, (int)active_rec_count + 10);
   ret = db_goto(tid, which);
   db_io_code_string(ret, mes);
   log_file_date("c_goto:r=%ld,rc=%s", which, mes);
}

void c_count(void)
{
   /* Test 'count' function. */

   char mes[50];
   long active_rec_count, deleted_rec_count;
   int ret;

   ret = db_count(tid, &active_rec_count, &deleted_rec_count);
   db_io_code_string(ret, mes);
   log_file_date("c_count:ac=%ld,dc=%ld,rc=%s", active_rec_count, 
                 deleted_rec_count, mes);
}

void c_put_field(void)
{
   /* Test 'put_field' function by writing a random field with
      a random number of digits. */

   char mes[50];
   int ret;
   int fnum;

   /* pick a random number between 0 and 254 for field number */

   fnum = rnd(0, 254);

   /* build field */

   c_put_data();
   ret = db_put_field(tid, fnum, data);
   db_io_code_string(ret, mes);
   log_file_date("c_put_field:f=%d,d=%s,l=%d,rc=%s", fnum, data,
                 strlen(data), mes);
}

void c_put_subfield(void)
{
   /* Test 'put.subfield' function. */

   char mes[50];
   int ret;
   int fnum, sfnum;

   /* pick a random number between 0 and 254 for field number and
      0 to 9 for subfield number */

   fnum = rnd(0, 254);
   sfnum = rnd(0, 9);

   /* build subfield */

   c_put_data();
   ret = db_put_subfield(tid, fnum, sfnum, data);
   db_io_code_string(ret, mes);
   log_file_date("c_put_subfield:f=%d,sf=%d,d=%s,l=%d,rc=%s", fnum, sfnum,
                 data, strlen(data), mes);
}

void c_put_subsubfield(void)
{
   /* Test 'put.subsubfield' function. */

   char mes[50];
   int ret;
   int fnum, sfnum, ssfnum;

   /* pick a random number between 0 and 254 for field number,
      0 to 9 for subfield number and subsubfield number */

   fnum = rnd(0, 254);
   sfnum = rnd(0, 9);
   ssfnum = rnd(0, 9);

   /* build subsubfield */

   c_put_data();
   ret = db_put_subsubfield(tid, fnum, sfnum, ssfnum, data);
   db_io_code_string(ret, mes);
   log_file_date("c_put_subsubfield:f=%d,sf=%d,ssf=%d,d=%s,l=%d,rc=%s", 
                 fnum, sfnum, ssfnum, data, strlen(data), mes);
}

void c_write(void)
{
   /* Test 'write' function. */

   char mes[50];
   int ret;

   ret = db_write(tid);
   db_io_code_string(ret, mes);
   log_file_date("c_write:rc=%s", mes);
}

void c_delete(void)
{
   /* Test 'delete' function. */

   char mes[50];
   int ret;

   ret = db_delete(tid);
   db_io_code_string(ret, mes);
   log_file_date("c_delete:rc=%s", mes);
}

void c_get_delete_flag(void)
{
   /* Test 'get_del_flag' function. */

   char mes[50];
   int ret;
   int del_flag;

   ret = db_get_delete_flag(tid, &del_flag);
   db_io_code_string(ret, mes);
   log_file_date("c_get_del_flag:f=%d,rc=%s", del_flag, mes);
}

void c_set_delete_flag(void)
{
   /* Test 'set_del_flag' function. */

   char mes[50];
   int ret;
   int del_flag;

   del_flag = rnd(0, 2);         /* generate illegal values on purpose */
   ret = db_set_delete_flag(tid, del_flag);
   db_io_code_string(ret, mes);
   log_file_date("c_set_delete_flag:f=%d,rc=%s", del_flag, mes);
}

void c_get_nfields(void)
{
   /* Test 'get_nfields' function. */

   char mes[50];
   int ret;
   int nfields;

   ret = db_get_nfields(tid, &nfields);
   db_io_code_string(ret, mes);
   log_file_date("c_nfields:f=%d,rc=%s", nfields, mes);
}

void c_get_nsubfields(void)
{
   /* Test 'get.nsubfields' function. */

   char mes[50];
   int ret;
   int nfields, nsubfields;
   int which;

   ret = db_get_nfields(tid, &nfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_nsubfields:nfields,rc=%s", mes);
      return;
      }

   which = (nfields > 1) ? rnd(1, nfields) : 1;
   ret = db_get_nsubfields(tid, which, &nsubfields);
   db_io_code_string(ret, mes);
   log_file_date("c_get_nsubfields:f=%d,nsubfields=%d,rc=%s", which,
                 nsubfields, mes);
}

void c_get_nsubsubfields(void)
{
   /* Test 'get.nsubsubfields' function. */

   char mes[50];
   int ret;
   int nfields, nsubfields, nsubsubfields;
   int which_field, which_subfield;

   ret = db_get_nfields(tid, &nfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_nsubsubfields:nfields,rc=%s", mes);
      return;
      }

   which_field = (nfields > 1) ? rnd(1, nfields) : 1;
   ret = db_get_nsubfields(tid, which_field, &nsubfields);

   if (ret != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_get_nsubsubfields:nsubfields,rc=%s", mes);
      return;
      }

   which_subfield = (nsubfields > 1) ? rnd(1, nsubfields) : 1;
   ret = db_get_nsubsubfields(tid, which_field, which_subfield, 
                              &nsubsubfields);
   db_io_code_string(ret, mes);
   log_file_date("c_get_nsubsubfields:f=%d,sf=%d,nsubsubfields=%d,rc=%s", 
                 which_field, which_subfield, nsubsubfields, mes);
}

void c_get_rec_num(void)
{
   /* Test 'get_rec_num' function. */

   char mes[50];
   int ret;
   long rec_num;

   ret = db_get_rec_num(tid, &rec_num);
   db_io_code_string(ret, mes);
   log_file_date("c_get_rec_num:r=%ld,rc=%s", rec_num, mes);
}

void c_get_pos(void)
{
   /* Test 'get_pos' function. */

   char mes[50];
   int ret;
   long pos;

   ret = db_get_pos(tid, &pos);
   db_io_code_string(ret, mes);
   log_file_date("c_get_pos:p=%ld,rc=%s", pos, mes);
}

void c_get_change_rec_flag(void)
{
   /* Test 'get_change_rec_flag' function. */

   char mes[50];
   int ret;
   int flag;

   ret = db_get_change_rec_flag(tid, &flag);
   db_io_code_string(ret, mes);
   log_file_date("c_get_change_rec_flag:f=%d,rc=%s", flag, mes);
}

void c_set_change_rec_flag(void)
{
   /* Test 'set_change_rec_flag' function. */

   char mes[50];
   int ret;
   int rec_flag;

   rec_flag = rnd(0, 2);         /* generate illegal values on purpose */
   ret = db_set_change_rec_flag(tid, rec_flag);
   db_io_code_string(ret, mes);
   log_file_date("c_set_change_rec_flag:f=%d,rc=%s", rec_flag, mes);
}

void c_pack(void)
{
   /* Test 'pack' function. */

   char mes[50];
   int ret;

   ret = db_pack(tid);
   db_io_code_string(ret, mes);
   log_file_date("c_pack:rc=%s", mes);
}

void c_new(void)
{
   /* Test 'new' function. */

   char mes[50];
   int ret;

   ret = db_new(tid);
   db_io_code_string(ret, mes);
   log_file_date("c_new:rc=%s", mes);
}

void c_find(void)
{
   /* Test 'find' function by creating a random field and
      searching for it. */

   char mes[50];
   char fdata_char[25];
   int ret;
   int fdata;
   int fnum;
   int cs_flag;

   /* pick a random number between 0 and 399 for field data, we
      intentionally have the maximum range too large */

   fdata = rnd(0, 399);

   /* decide whether to use case sensitivity, can also generate
      illegal values (0-2) */

   cs_flag = rnd(0, 2);
   sprintf(fdata_char, "%d", fdata);
   ret = db_find(tid, cs_flag, fdata_char, &fnum);
   db_io_code_string(ret, mes);
   log_file_date("c_find:cs=%d,fd=%s,fn=%d,rc=%s", cs_flag, fdata_char,
                 fnum, mes);
}

void c_find_field(void)
{
   /* Test 'find_field' function by creating a random field and
      searching for it. */

   char mes[50];
   char fdata_char[25];
   int ret;
   int fdata;
   int fnum;
   int cs_flag;

   /* pick a random number between 0 and 399 for field data, we
      intentionally have the maximum range too large and a possible
      zero value */

   fdata = rnd(0, 399);

   /* decide whether to use case sensitivity, can also generate
      illegal values (0-2) */

   cs_flag = rnd(0, 2);

   /* decide on which field to search, can also generate illegal values */

   fnum = rnd(0, 19);

   sprintf(fdata_char, "%d", fdata);
   ret = db_find_field(tid, cs_flag, fdata_char, fnum);
   db_io_code_string(ret, mes);
   log_file_date("c_find_field:cs=%d,fd=%s,fn=%d,rc=%s", cs_flag,
                 fdata_char, fnum, mes);
}

void c_find_part(void)
{
   /* Test 'find_part' function by creating a random field and
      searching for it. */

   char mes[50];
   char fdata_char[25];
   int ret;
   int fdata;
   int fnum;
   int cs_flag;

   /* pick a random number between 0 and 399 for field data, we
      intentionally have the maximum range too large */

   fdata = rnd(0, 399);

   /* decide whether to use case sensitivity, can also generate
      illegal values (0-2) */

   cs_flag = rnd(0, 2);
   sprintf(fdata_char, "%d", fdata);
   ret = db_find_part(tid, cs_flag, fdata_char, &fnum);
   db_io_code_string(ret, mes);
   log_file_date("c_find_part:cs=%d,fd=%s,fn=%d,rc=%s", cs_flag,
                 fdata_char, fnum, mes);
}

void c_find_field_part(void)
{
   /* Test 'find_field_part' function by creating a random field and
      searching for it. */

   char mes[50];
   char fdata_char[25];
   int ret;
   int fdata;
   int fnum;
   int cs_flag;

   /* pick a random number between 0 and 399 for field data, we
      intentionally have the maximum range too large and a possible
      zero value */

   fdata = rnd(0, 399);

   /* decide whether to use case sensitivity, can also generate
      illegal values (0-2) */

   cs_flag = rnd(0, 2);

   /* decide on which field to search, can also generate illegal values */

   fnum = rnd(0, 20);

   sprintf(fdata_char, "%d", fdata);
   ret = db_find_field_part(tid, cs_flag, fdata_char, fnum);
   db_io_code_string(ret, mes);
   log_file_date("c_find_field_part:cs=%d,fd=%s,fn=%d,rc=%s",
                 cs_flag, fdata_char, fnum, mes);
}

void c_get_rec_count(void)
{
   /* Test 'get_record_count' function. */

   char mes[50];
   long active_rec_count, deleted_rec_count;
   int ret;

   ret = db_get_rec_count(tid, &active_rec_count, &deleted_rec_count);
   db_io_code_string(ret, mes);
   log_file_date("c_get_record_count:ac=%ld,dc=%ld,rc=%s", active_rec_count, 
                 deleted_rec_count, mes);
}

void c_get_enf_change_rec_flag(void)
{
   /* Test 'get_enf_change_rec_flag' function. */

   char mes[50];
   int ret;
   int flag;

   ret = db_get_enf_change_rec_flag(tid, &flag);
   db_io_code_string(ret, mes);
   log_file_date("c_get_enf_change_rec_flag:f=%d,rc=%s", flag, mes);
}

void c_set_enf_change_rec_flag(void)
{
   /* Test 'set_enf_change_rec_flag' function. */

   char mes[50];
   int ret;
   int rec_flag;

   rec_flag = rnd(0, 2);         /* generate illegal values on purpose */
   ret = db_set_enf_change_rec_flag(tid, rec_flag);
   db_io_code_string(ret, mes);
   log_file_date("c_set_enf_change_rec_flag:f=%d,rc=%s", rec_flag, mes);
}

void c_get_is_table_locked(void)
{
   /* Test 'get_is_table_locked' function. */

   char mes[50];
   int ret;
   int flag;

   ret = db_get_is_table_locked(tid, &flag);
   db_io_code_string(ret, mes);
   log_file_date("c_get_is_table_locked:f=%d,rc=%s", flag, mes);
}

void c_set_is_table_locked(void)
{
   /* Test 'set_is_table_locked' function. */

   char mes[50];
   int ret;
   int rec_flag;

   rec_flag = rnd(0, 2);         /* generate illegal values on purpose */
   ret = db_set_is_table_locked(tid, rec_flag);
   db_io_code_string(ret, mes);
   log_file_date("c_set_is_table_locked:f=%d,rc=%s", rec_flag, mes);
}

void c_new_table(void)
{
   /* Test 'new_table' function. Available in single user mode
      only. */

#ifdef MULTIUSER
   log_file_date("c_new_table:command not implemented in c/s mode");
   return;
#else
   char *tmp_path, *dest_table_name;
   char rname[13], mes[50];
   int tid, tid_flag, ret;

   /* get temporary path from config data */

   if ((tmp_path = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
      {
      db_io_code_string(DBENG_MEMORY_FAIL, mes);
      log_file_date("c_new_table:alloc fail 'tmp_path',rc=%s", mes);
      return;
      }

   if ((ret = db_config_get_tmp_path(tmp_path)) != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_new_table:error getting config tmp path,rc=%s", mes);
      free(tmp_path);
      return;
      }

   /* generate a unique file name */

   if (!unique(tmp_path, rname))
      {
      db_io_code_string(DBENG_CANNOT_CREATE_TABLE, mes);
      log_file_date("c_new_table:error generating unique file name,rc=%s", mes);
      free(tmp_path);
      return;
      }

   if ((dest_table_name = malloc(1024)) == NULL)
      {
      db_io_code_string(DBENG_MEMORY_FAIL, mes);
      log_file_date("c_new_table:alloc fail 'dest_table_name',rc=%s", mes);
      free(tmp_path);
      return;
      }

   /* build complete path and file name */

   sprintf(dest_table_name, "%s%c%s", tmp_path, PATH_SEP, rname);
   free(tmp_path);
   tid_flag = rnd(0, 2);
   tid = tid_flag;
   ret = db_new_table(dest_table_name, &tid);
   db_io_code_string(ret, mes);
   log_file_date("c_new_table:dtn=%s,tf=%d,tid=%d,rc=%s", dest_table_name,
                 tid_flag, tid, mes);

   if (tid_flag)
      {
      ret = db_close(tid);

      if (ret != DBENG_OK)
         {
         db_io_code_string(ret, mes);
         log_file_date("c_new_table:close failed,rc=%s", mes);
         }
      }

   unlink(dest_table_name);
   free(dest_table_name);
#endif
}

void c_config_get_tmp_path(void)
{
   /* Test the 'config_get_tmp_path' function. */

   char *tmp_path, mes[50];
   int ret;

   if ((tmp_path = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
      {
      db_io_code_string(DBENG_MEMORY_FAIL, mes);
      log_file_date("c_config_get_tmp_path:alloc fail 'tmp_path',rc=%s", mes);
      return;
      }

   if ((ret = db_config_get_tmp_path(tmp_path)) != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_config_get_tmp_path:error getting config tmp path,rc=%s", mes);
      free(tmp_path);
      return;
      }

   db_io_code_string(ret, mes);
   log_file_date("c_config_get_tmp_path:path=%s,rc=%s", tmp_path, mes);
   free(tmp_path);
}

void c_config_get_log(void)
{
   /* Test the 'config_get_log' function. */

   char *slog, mes[50];
   int ret;

   if ((slog = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
      {
      db_io_code_string(DBENG_MEMORY_FAIL, mes);
      log_file_date("c_config_get_log:alloc fail 'slog',rc=%s", mes);
      return;
      }

   if ((ret = db_config_get_log(slog)) != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_config_get_log:error getting config log,rc=%s", mes);
      free(slog);
      return;
      }

   db_io_code_string(ret, mes);
   log_file_date("c_config_get_log:log=%s,rc=%s", slog, mes);
   free(slog);
}

void c_config_get_session(void)
{
   /* Test the 'config_get_session' function. */

   char *sessiont, mes[50];
   int ret;

   if ((sessiont = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
      {
      db_io_code_string(DBENG_MEMORY_FAIL, mes);
      log_file_date("c_config_get_session:alloc fail 'sessiont',rc=%s", mes);
      return;
      }

   if ((ret = db_config_get_session(sessiont)) != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_config_get_session:error getting config session table,"
                    "rc=%s", mes);
      free(sessiont);
      return;
      }

   db_io_code_string(ret, mes);
   log_file_date("c_config_get_session:session table=%s,rc=%s", sessiont, mes);
   free(sessiont);
}

void c_config_get_catalog(void)
{
   /* Test the 'config_get_catalog' function. */

   char *catalogt, mes[50];
   int ret;

   if ((catalogt = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
      {
      db_io_code_string(DBENG_MEMORY_FAIL, mes);
      log_file_date("c_config_get_catalog:alloc fail 'catalogt',rc=%s", mes);
      return;
      }

   if ((ret = db_config_get_catalog(catalogt)) != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("c_config_get_catalog:error getting config catalog table,"
                    "rc=%s", mes);
      free(catalogt);
      return;
      }

   db_io_code_string(ret, mes);
   log_file_date("c_config_get_catalog:catalog table=%s,rc=%s", catalogt, mes);
   free(catalogt);
}

void c_config_get_log_flag(void)
{
   /* Test the 'config_get_log_flag' function. */

   char mes[50];
   int ret;
   int flag;

   ret = db_config_get_log_flag(&flag);
   db_io_code_string(ret, mes);
   log_file_date("c_config_get_log_flag:f=%d,rc=%s", flag, mes);
}

void c_config_get_session_flag(void)
{
   /* Test the 'config_get_session_flag' function. */

   char mes[50];
   int ret;
   int flag;

   ret = db_config_get_session_flag(&flag);
   db_io_code_string(ret, mes);
   log_file_date("c_config_get_session_flag:f=%d,rc=%s", flag, mes);
}

void c_config_get_catalog_flag(void)
{
   /* Test the 'config_get_catalog_flag' function. */

   char mes[50];
   int ret;
   int flag;

   ret = db_config_get_catalog_flag(&flag);
   db_io_code_string(ret, mes);
   log_file_date("c_config_get_catalog_flag:f=%d,rc=%s", flag, mes);
}

void c_config_get_replicate_flag(void)
{
   /* Test the 'config.get.replicate.flag' function. */

   char mes[50];
   int ret;
   int flag;

   ret = db_config_get_replicate_flag(&flag);
   db_io_code_string(ret, mes);
   log_file_date("c_config_get_replicate_flag:f=%d,rc=%s", flag, mes);
}

void c_config_get_version(void)
{
   /* Test the 'config_get_version' function. */

   char version[50], mes[50];
   int ret;

   ret = db_config_get_version(version);
   db_io_code_string(ret, mes);
   log_file_date("c_config_get_version:v=%s,rc=%s", version, mes);
}

void c_get_open_table_list(void)
{
   /* Test 'get_open_table_list' function. */

   char mes[50];
   int ret, nwords;

   if ((ret = db_get_open_table_list((char *)NULL, data)) == DBENG_OK)
      {
      nwords = ll_words(data, DBENG_LIST_DELIM);
      log_file_date("c_get_open_table_list:%d open table(s)", nwords);
      }

   db_io_code_string(ret, mes);
   log_file_date("c_get_open_table_list:rc=%s", mes);
}

void c_version(void)
{
   /* Get 'Bbuuzzb' server version ID. Applies to multiuser
      only. */
      
#ifndef MULTIUSER
   log_file_date("c_version:does not apply to single user");
   return;
#else
   char mes[128];
   int ret;
   
   data[0] = EOS;
   ret = db_version(data);
   db_io_code_string(ret, mes);
   log_file_date("c_version:ver=%s,rc=%s", data, mes);
#endif   
}

void c_exist(void)
{
   /* Test the 'exist' function. */

   char mes[128], fname[128];
   int test_prim, ret, exist_flag;

   /* pick a random number between 0 and 1 */

   test_prim = rnd(0, 1);

   if (test_prim)
      strcpy(fname, ds_prim);
   else
      strcpy(fname, "zzz");

   ret = db_exist(fname, &exist_flag);
   db_io_code_string(ret, mes);
   log_file_date("c_exist:fname=%s,exist_flag=%d,rc=%s", fname, 
                 exist_flag, mes);
}

void c_get_catalog_list(void)
{
   /* Test 'get_catalog_list' function. */

   char mes[50];
   int ret, nwords;

   if ((ret = db_get_catalog_list((char *)NULL, data)) == DBENG_OK)
      {
      nwords = ll_words(data, DBENG_LIST_DELIM);
      log_file_date("c_get_catalog_list:%d catalog entry/entries", nwords);
      }

   db_io_code_string(ret, mes);
   log_file_date("c_get_catalog_list:rc=%s", mes);
}

void c_sort(void)
{
   /* Test the 'sort' command. */

   char mes[50], spec[5], mname[] = "c_sort";
   int ret, which_type;

   /* pick a random number between 0 and 3 */

   which_type = rnd(0, 3);

   switch(which_type)
      {
      case 0:
         /* just field 1 ascending */

         strcpy(spec, "1");
         break;

      case 1:
         /* field 1 descending */

         strcpy(spec, "1,d");
         break;

      case 2:
         /* field 1 and field two both ascending */

         strcpy(spec, "1^2");
         break;

      case 3:
         /* illegal */

         strcpy(spec, "1,z");
         break;
      };

   ret = db_sort(tid, spec);
   db_io_code_string(ret, mes);
   log_file_date("%s:spec=%s,rc=%s", mname, spec, mes);
}

void c_get_sort_mem(void)
{
   /* Test the 'get.sort.mem' command. */

   char mes[50];
   long mem;
   int ret;

   ret = db_get_sort_max_mem(tid, &mem);
   db_io_code_string(ret, mes);
   log_file_date("c_get_sort_mem:sort mem=%ld,rc=%s", mem, mes);
}

void c_set_sort_mem(void)
{
   /* Test the 'set.sort.mem' command. */

   char mes[50];
   long mem;
   int ret;

   /* generate a random long in the range 1700-200000 (actual allowable
      range is >= 2000) */

#ifndef OS_DOS
   mem = (long)rnd(1700, 200000);
#else
   mem = (long)rnd(1700, 32766);
#endif
   ret = db_set_sort_max_mem(tid, mem);
   db_io_code_string(ret, mes);
   log_file_date("c_set_sort_mem:sort mem=%ld,rc=%s", mem, mes);
}

void c_get_sort_open_bin(void)
{
   /* Test the 'get.sort.open.bin' command. */

   char mes[50];
   int max_bin;
   int ret;

   ret = db_get_sort_max_open_bin(tid, &max_bin);
   db_io_code_string(ret, mes);
   log_file_date("c_get_sort_open_bin:open bin=%d,rc=%s", max_bin, mes);
}

void c_set_sort_open_bin(void)
{
   /* Test the 'set.sort.open.bin' command. */

   char mes[50];
   int max_bin;
   int ret;

   /* generate a random int in the range 0-29 (actual allowable
      range is >= 3) */

   max_bin = rnd(0, 29);
   ret = db_set_sort_max_open_bin(tid, max_bin);
   db_io_code_string(ret, mes);
   log_file_date("c_set_sort_open_bin:open_bin=%d,rc=%s", max_bin, mes);
}

void c_delete_field(void)
{
   /* Test 'delete.field' function by deleting a random field. */

   char mes[50];
   int ret;
   int fnum;

   /* pick a random number between 0 and 254 for field number */

   fnum = rnd(0, 254);
   ret = db_delete_field(tid, fnum);
   db_io_code_string(ret, mes);
   log_file_date("c_delete_field:f=%d,rc=%s", fnum, mes);
}

void c_delete_subfield(void)
{
   /* Test 'delete.subfield' function by deleting a random subfield. */

   char mes[50];
   int ret;
   int fnum, sfnum;

   /* pick a random number between 0 and 254 for field number
      and zero to 9 for subfield number */

   fnum = rnd(0, 254);
   sfnum = rnd(0, 9);
   ret = db_delete_subfield(tid, fnum, sfnum);
   db_io_code_string(ret, mes);
   log_file_date("c_delete_subfield:f=%d,sf=%d,rc=%s", fnum, sfnum, mes);
}

void c_delete_subsubfield(void)
{
   /* Test 'delete.subsubfield' function by deleting a random subsubfield. */

   char mes[50];
   int ret;
   int fnum, sfnum, ssfnum;

   /* pick a random number between 0 and 254 for field number
      and zero to 9 for subfield and subsubfield number */

   fnum = rnd(0, 254);
   sfnum = rnd(0, 9);
   ssfnum = rnd(0, 9);
   ret = db_delete_subsubfield(tid, fnum, sfnum, ssfnum);
   db_io_code_string(ret, mes);
   log_file_date("c_delete_subsubfield:f=%d,sf=%d,ssf=%d,rc=%s", fnum, sfnum, 
                 ssfnum, mes);
}

void c_trans_num(void)
{
   /* Get 'Bbuuzzb' server transaction count. Applies to multiuser
      only. */
      
   char mname[] = "c_trans_num";

#ifndef MULTIUSER
   log_file_date("%s:does not apply to single user", mname);
   return;
#else
   char mes[128];
   long trans_num;
   int ret;
   
   ret = db_trans_num(&trans_num);
   db_io_code_string(ret, mes);
   log_file_date("%s:trans_num=%ld,rc=%s", mname, trans_num, mes);
#endif   
}

void c_connect_num(void)
{
   /* Get 'Bbuuzzb' server connection count. Applies to multiuser
      only. */
      
   char mname[] = "c_connect_num";

#ifndef MULTIUSER
   log_file_date("%s:does not apply to single user", mname);
   return;
#else
   char mes[128];
   int connect_num;
   int ret;
   
   ret = db_connect_num(&connect_num);
   db_io_code_string(ret, mes);
   log_file_date("%s:connect_num=%d,rc=%s", mname, connect_num, mes);
#endif   
}

void c_get_autopack(void)
{
   /* Test 'get_autopack' function. */

   char mes[50];
   int ret;
   int pval;

   ret = db_get_autopack(tid, &pval);
   db_io_code_string(ret, mes);
   log_file_date("c_get_autopack:p=%d,rc=%s", pval, mes);
}

void c_set_autopack(void)
{
   /* Test 'set_autopack' function. */

   char mes[50];
   int ret;
   int pval;

   pval = rnd(0, 10);
   ret = db_set_autopack(tid, pval);
   db_io_code_string(ret, mes);
   log_file_date("c_set_autopack:p=%d,rc=%s", pval, mes);
}

void c_config_client_get_log(void)
{
   /* Test the 'config_client_get_log' function. */

   char mname[] = "c_config_client_get_log";

#ifndef MULTIUSER
   log_file_date("%s:does not apply to single user", mname);
   return;
#else
   char *slog, mes[50];
   int ret;

   if ((slog = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
      {
      db_io_code_string(DBENG_MEMORY_FAIL, mes);
      log_file_date("%s:alloc fail 'slog',rc=%s", mname, mes);
      return;
      }

   if ((ret = db_config_client_get_log(slog)) != DBENG_OK)
      {
      db_io_code_string(ret, mes);
      log_file_date("%s:error getting config log,rc=%s", mname, mes);
      free(slog);
      return;
      }

   db_io_code_string(ret, mes);
   log_file_date("%s:log=%s,rc=%s", mname, slog, mes);
   free(slog);
#endif
}

void c_config_client_get_log_flag(void)
{
   /* Test the 'config_client_get_log_flag' function. */

   char mname[] = "c_config_client_get_log_flag";

#ifndef MULTIUSER
   log_file_date("%s:does not apply to single user", mname);
   return;
#else
   char mes[50];
   int ret;
   int flag;

   ret = db_config_client_get_log_flag(&flag);
   db_io_code_string(ret, mes);
   log_file_date("%s:f=%d,rc=%s", mname, flag, mes);
#endif
}

void c_put_data(void)
{
   /* Place a random number of digits into the 'data' string. */

   char ch;
   int ndigits;
   int i;

   /* generate between 1 and 100 digits */

   ndigits = rnd(1, 100);

   /* build */

   data[0] = EOS;

   for(i = 0; i < ndigits; i++)
      {
      ch = rnd(48, 57); /* random digit */
      data[i] = ch;
      }

   data[ndigits] = EOS;
}

void term_app(void)
{
   // Prepare to terminate the application. Shutdown all IPC API's.

   db_end();

#ifndef OS_DOS
   if (is_sys_log_active())
      sys_log_end();
#endif

#ifndef MULTIUSER
   // single user
#ifdef DEBUG
   // with 'DEBUG' on
#ifdef IPC_TCP
   // with TCP IPC method
   sloc_term_api();
#ifdef OS_WIN32
   // under Windoze
   WSACleanup();
#endif // Windoze
#endif // TCP IPC
#endif // DEBUG
#else  // multiuser
#ifdef IPC_TCP
   // with TCP IPC method
   sloc_term_api();
#endif
#ifdef OS_WIN32
   // under Windoze
   WSACleanup();
#endif
#endif

#ifndef OS_DOS
   (void)appinit_remove_name();
#endif

   if (is_log_active())
      log_end();
}
