/* t4mul.c (c)Copyright Sequiter Software Inc., 1990-1993. All rights reserved. */
/* Tests Code Base multi-user. */
/* Run program from exactly two workstations. */
/* OR run program from exactly one Windows station */

#include "d4all.h"
#ifdef __TURBOC__
#pragma hdrstop
#endif

#include "t4test.h"

#ifndef S4INDEX_OFF

#ifdef S4WINDOWS
/* currently just exclude conio */
#else
#ifdef S4UNIX
#ifdef S4CURSES
/*       #include <curses.h>  */
#else
/*       #include <tinfo.h>   */
#endif
#else
#include <conio.h>
#endif
#endif

#define WORK_WIDTH 10

static FIELD4INFO co_fields[] =
{
{ "COMMAND", 'C', WORK_WIDTH,0 },
{ "PARM", 'C', 8, 0 },
{ 0,0,0,0 },
} ;

static FIELD4INFO wo_fields[] =
{
{ "WORK", 'C', WORK_WIDTH,0 },
{ 0,0,0,0 },
} ;

static TAG4INFO wo_tags_one[] =
{
{ "TAG1", "WORK", 0,0,0 },
{ "TAG2", "WORK", 0,0,0 },
{ 0,0,0,0,0 },
} ;

static TAG4INFO wo_tags_two[] =
{
{ "TAG3", "WORK", 0,0,0 },
{ "TAG4", "WORK", 0,0,0 },
{ 0,0,0,0,0 },
} ;

static FIELD4INFO result_fields[] =
{
{ "RESULT", 'C', 8,0 },
{ 0,0,0,0 },
} ;

#ifdef S4LOG
static FIELD4INFO  log_fields[] =
{
{ "ID",   'C', 12,0 },
{ "LOG1", 'C', 20,0 },
{ "LOG2", 'C', 20,0 },
{ 0,0,0,0 },
} ;
#endif

D4DISPLAY  *disp ;
CODE4     cb;
DATA4     *co_ptr, *wo_ptr, *re_ptr ;
DATA4     *log_ptr = 0 ;
INDEX4    *w1_ptr, *w2_ptr ;
FIELD4    *command_field, *parm, *work, *result, *log_id, *logic1, *logic2 ;
char       *log_id_ptr = 0 ;
char       seek_str[WORK_WIDTH+1] ;
int        rc ;

static int   send_com( char *, char *) ;
static int   int_ret( long, int ) ;
static void  ch_base_locked( long,int);
static int   command_sender(void) ;
static int   work_doer(void) ;
static void  d_lock(void) ;
static void  d_seek(void) ;
static void  d_skip(void) ;
static void  d_write(void) ;
static void  d_append(void) ;
static void  d_unlock(void) ;
static void  i_lock(void) ;
static void  d_append_one(void) ;
static int   seek_recs( long ) ;
static int   write_recs( long ) ;
static int   append_recs( long ) ;
static int   skip_recs( long ) ;
static int   result_int( int) ;
int test_multi( void ) ;
static int  test_with_mem_check( void ) ;

#ifdef S4WINDOWS
/* transfer control */
static int t4sleep( long dummy )   
{
  if ( PeekMessage( &disp->msg, disp->hWnd, 0, 0, PM_NOREMOVE ) )
  {
    if ( disp->msg.message == 275 )  /* remove the timer messages */
    {
      if ( !GetMessage( &disp->msg, disp->hWnd, 0, 0 ) )
        return 1 ;
    }
    else
    {
      if ( !GetMessage( &disp->msg, disp->hWnd, 0, 0 ) )
        return 1 ;
      TranslateMessage( (LPMSG)&disp->msg ) ;
      if( disp->msg.message == WM_DESTROY )
        return -1 ;
      DispatchMessage( (LPMSG)&disp->msg ) ;
    }
  }
  return 0 ;
}
#else
static int  t4sleep(long) ;
#endif

/* Number of Records per Buffer for T4MUL_WO.dbf */
#define  REC_PER_BUF  2L

extern void  logger( char *, char * ) ;

int first = 1 ;
int in_logger = 0 ;


void logger( char *p1, char *p2 )
{
  if ( in_logger || log_ptr == 0 )
    return ;
  in_logger = 1 ;

  if ( d4lock_file( log_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  if ( first )
  {
    first = 0 ;
    if ( d4zap( log_ptr, 1L, d4reccount( log_ptr ) ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;
  }

  if ( d4append_start(log_ptr, 0) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  if ( log_id_ptr == (char *) 0 )
    f4assign( log_id, "UNKNOWN" ) ;
  else
    f4assign( log_id, log_id_ptr ) ;

  f4assign( logic1, p1 ) ;
  f4assign( logic2, p2 ) ;

  if ( d4append( log_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  if ( d4flush( log_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;
  if ( d4unlock( log_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;
  in_logger = 0 ;
}

#ifdef S4OS2
extern unsigned far pascal DOSSLEEP ( unsigned long );

static int  t4sleep( long n_ms )
{
  DOSSLEEP( (unsigned long) n_ms ) ;
  return 0 ;
}
#else
#ifndef S4WINDOWS
static int  t4sleep( long n_ms )
{
  while( n_ms-- > 0 ) ;
  return 0 ;
}
#endif
#endif

int test_multi()
{
  int  rc ;

#ifdef S4WINDOWS
  cb.lock_attempts = 1 ;     /* don't lock up a Window's application */
#endif

  cb.open_error = 0 ;
#ifdef N4OTHER
  cb.auto_open = 0 ;
#endif

  co_ptr = d4open( &cb, "T4MUL_CO" ) ;
  if ( co_ptr == 0 )
  {
    if ( cb.error_code == r4no_open )
    {
      d4display_str( disp,  "        Creating T4MUL_CO . . .  ", 1 ) ;
      co_ptr = d4create( &cb, "T4MUL_CO", co_fields, 0 ) ;
      if ( co_ptr == 0 )
        e4severe( e4result, "t4mul.c" ) ;
    }
    else
      e4severe( e4result, "t4mul.c" ) ;
  }

  command_field = d4field( co_ptr, "COMMAND" ) ;
  parm = d4field( co_ptr, "PARM" ) ;

  if ( command_field == 0 || parm == 0 )
    e4severe( e4result, "t4mul.c" ) ;

  wo_ptr = d4open( &cb, "T4MUL_WO" ) ;
  if (wo_ptr == 0 )
  {
    if ( cb.error_code == r4no_open )
    {
      d4display_str( disp,  "        Creating T4MUL_WO . . .  ", 1 ) ;
      wo_ptr = d4create( &cb, "T4MUL_WO", wo_fields, 0 ) ;
      if ( wo_ptr == 0 )
        e4severe( e4result, "t4mul.c" ) ;
    }
    else
      e4severe( e4result, "t4mul.c" ) ;
  }

  work = d4field( wo_ptr, "WORK" ) ;
  if ( work == 0 )
    e4severe( e4result, "t4mul.c" ) ;

  re_ptr = d4open( &cb, "T4MUL_RE" ) ;
  if (re_ptr == 0 )
  {
    if ( cb.error_code == r4no_open )
    {
      re_ptr = d4create( &cb, "T4MUL_RE", result_fields, 0 ) ;
      if ( re_ptr == 0 )
        e4severe( e4result, "t4mul.c" ) ;
      d4display_str( disp,  "        Creating T4MUL_RE . . .  ", 1 ) ;
    }
    else
      e4severe( e4result, "t4mul.c" ) ;
  }

  result = d4field( re_ptr, "RESULT" ) ;
  if ( result == 0 )
    e4severe( e4result, "t4mul.c" ) ;

#ifdef S4LOG
  if ( log_ptr = d4open( &cb, "T4MUL_LO" ) == 0 )
  {
    if ( cb.error_code == r4no_open )
    {
      d4display_str( disp,  "        Creating T4MUL_LO . . .  ", 1 ) ;
      log_ptr = d4create( &cb, "T4MUL_LO", log_fields, 0 ) ;
      if ( log_ptr == 0 )
        e4severe( e4result, "t4mul.c" ) ;
    }
    else
      e4severe( e4result, "t4mul.c" ) ;
  }

  log_id = d4field( log_ptr, "ID" ) ;
  logic1 = d4field( log_ptr, "LOG1" ) ;
  logic2 = d4field( log_ptr, "LOG2" ) ;

  logger( "First Log", "" ) ;

#endif

  d4display_str( disp,  "        Databases Open.  ", 1 ) ;

#ifndef S4WINDOWS
  cb.lock_attempts = 1 ;
#endif
  if ( (rc = d4lock_file( wo_ptr )) < 0 )
    e4severe( e4result, "t4mul.c" ) ;

#ifndef S4WINDOWS
  cb.lock_attempts = -1 ;
#endif

  if ( rc == r4locked )
  {
    log_id_ptr = "Commander" ;
    d4display_str( disp,  "        Commander Session.  ", 1 ) ;

    if ( d4zap( co_ptr, 1L, d4reccount( co_ptr ) ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;
    for (;;)
    {
      if ( d4refresh( re_ptr ) != 0 )
        e4severe( e4result, "t4mul.c" ) ;
      if ( d4reccount( re_ptr ) == 0L )
        break ;
      if ( t4sleep( 50 ) < 0 )
        return -1 ;
    }
    for (;;)
    {
      if ( d4refresh( re_ptr ) != 0 )
        e4severe( e4result, "t4mul.c" ) ;
      if ( d4reccount( re_ptr ) > 0L )
        break ;
      if ( d4refresh( re_ptr ) != 0 )
        e4severe( e4result, "t4mul.c" ) ;
      if ( d4reccount( co_ptr ) == 0L )
        if ( send_com("UNLOCK ALL", "-1" ) )
          return 1 ;
      if ( t4sleep( 50 ) < 0 )
        return -1 ;
    }
    if ( command_sender() )
      return 1 ;
    d4display_str( disp,  "        Commander Finished.  ", 1 ) ;
  }
  else
  {
    log_id_ptr = "Worker   " ;
    d4display_str( disp,  "        Worker Session.  ", 1 ) ;

    if ( d4zap( wo_ptr, 1L, d4reccount( wo_ptr )) != 0 )
      e4severe( e4result, "t4mul.c" ) ;
    if ( d4zap( re_ptr, 1L, d4reccount( re_ptr ) ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;
    if (d4zap( co_ptr, 1L, d4reccount( co_ptr ) ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;

    cb.safety = 0 ;

    w1_ptr = i4create( wo_ptr, "T4MUL_W1", wo_tags_one ) ;
    if ( w1_ptr == 0 )
      e4severe( e4result, "t4mul.c: i4create" ) ;

    w2_ptr = i4create( wo_ptr, "T4MUL_W2", wo_tags_two ) ;
    if ( w2_ptr == 0 )
      e4severe( e4result, "t4mul.c: i4create" ) ;

    cb.safety = 1 ;

    if ( d4unlock( re_ptr ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;
    if ( d4unlock( co_ptr ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;

    if ( work_doer() )
      return 1 ;
    d4display_str( disp,  "        Worker Finished.  ", 1 ) ;
  }

  if ( d4close_all( &cb ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

#ifdef L4LOCK_CHECK
  if ( l4lock_check() != 0 )
    e4severe( e4result, "Not everything was unlocked" ) ;
#endif
  if ( cb.error_code < 0 )
    e4severe( e4result, "t4mul.c: cb.error_code" ) ;

  return 0 ;
}


static int send_com( char *com, char *com_parm )
{
  if ( d4display_quit( disp ) )  return 1 ;

  d4display_str( disp,  "        Sending-  Command: ", 1 ) ;
  d4display_str( disp, com, 0 ) ;
  d4display_str( disp,  "        Parm: ", 1 ) ;
  d4display_str( disp, com_parm, 0 ) ;
  d4display_str( disp, "      ", 1 ) ;

  if ( d4lock_file( co_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  if ( d4append_start( co_ptr, 0 ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;
  f4assign( command_field, com ) ;
  f4assign( parm, com_parm ) ;
  if ( d4append( co_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  if ( d4flush( co_ptr ) != 0 )
    e4severe( e4result, (char *) 0 ) ;
  if ( d4unlock( co_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  return 0 ;
}


static int int_ret(  long rec, int val )
{
  if ( d4display_quit( disp ) )
    return 1 ;

  d4display_str( disp, "        Getting Return Record . . . ", 1 ) ;
  d4display_num( disp, rec, 0 ) ;

  if ( d4unlock( re_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  if ( d4refresh( re_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;
  while ( d4reccount( re_ptr ) < rec )
  {
    if ( t4sleep(50) < 0 )
      return -1 ;
    if ( d4refresh( re_ptr ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;
  }

  if ( d4go( re_ptr, (long)rec ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;
  if ( f4int( result ) != val )
    e4severe( e4result, "t4mul.c" ) ;

  if ( d4unlock( re_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  d4display_str( disp, "        Received.", 1 ) ;
  return 0 ;
}


/* Check the locks

   Ran on worker data file to determine what locks another station
   has put on the worker data file.  This station none on the worker
   data file when this routine is called.

   rec_cd -  That Record Should be Locked
   -  If zero, no specific record is locked
   -  If the entire file should be is locked, this must be zero

   file_cd - Neg one and the whole file should be locked.
   - Zero and the reccount byte should be locked.
   - One and nothing is locked except perhaps a record.
   */
static void ch_base_locked(  long rec_cd, int file_cd )
{
  int  rc ;
  d4display_str( disp, "        Checking Database Locks . . .", 1 ) ;

#ifndef S4WINDOWS
  cb.lock_attempts = 1 ;
#endif

  rc = d4lock_file( wo_ptr ) ;
  if ( rc < 0 )
    e4severe( e4result, "t4mul.c:  d4lock_file" ) ;
  if ( d4unlock( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  if ( rc == r4locked )
  {
    if ( rec_cd == 0  &&  file_cd == 1 )
      e4severe( e4result, "t4mul.c:  Lock Error 0" ) ;
  }
  else
  {
    if (rec_cd != 0L  ||  file_cd != 1 )
      e4severe( e4result, "t4mul.c:  Lock Error 1" ) ;
  }

  if ( (rc = d4lock_append( wo_ptr ) ) < 0 )
    e4severe( e4result, "t4mul.c:  d4lock_append" ) ;
  if ( d4unlock( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  /* If record count bytes can be locked, 'worker' cannot have
     the whole file locked.   In addition, if the record count
     bytes cannot be locked, they better not be unlocked. */

  if ( rc == 0 && file_cd <= 0  ||   rc == r4locked && file_cd == 1 )
    e4severe( e4result, "t4mul.c:  Lock Error 2" ) ;

  if ( d4reccount( wo_ptr ) > 0L )
  {
    if ( (rc = d4lock( wo_ptr, 1L)) < 0 )
      e4severe( e4result, "t4mul.c:  d4lock" ) ;
    if ( d4unlock( wo_ptr ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;

    /* If record one can be locked, 'worker' cannot have the whole
       file locked.  If they cannot be locked, they better not be locked. */
    if ( rc == 0 && (file_cd == -1 || rec_cd == 1L) || rc == r4locked && file_cd != -1 && rec_cd != 1L)
      e4severe( e4result, "t4mul.c:  Lock Error 3" ) ;
  }

  if ( rec_cd > 0L )
  {
    /* If 'worker' has locked the record, we better not be able to lock it. */
    if ( (rc = d4lock( wo_ptr, rec_cd)) < 0 )
      e4severe( e4result, "t4mul.c:  d4lock" ) ;
    if ( d4unlock( wo_ptr ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;
    if ( rc == 0  )
      e4severe( e4result, "t4mul: Lock Error 4" ) ;
  }

#ifndef S4WINDOWS
  cb.lock_attempts = -1 ;
#endif
}


static int command_sender()
{
  int  i ;
  long count, r ;

  r = 1L ;

  if ( int_ret(r++, 0 ) ) return 1 ;
  ch_base_locked(0L, 1) ;

  /* Lock the work database, it will not be unlocked until
     the index files have been created. */
  d4lock_file( wo_ptr ) ;

  if ( d4opt_suspend( &cb ) != 0 )
    e4severe( e4result, "t4mul" ) ;
  w1_ptr = i4open( wo_ptr, "T4MUL_W1" ) ;
  w2_ptr = i4open( wo_ptr, "T4MUL_W2" ) ;
  if ( w1_ptr == 0 || w2_ptr == 0 )
    e4severe( e4result, "t4mul" ) ;
  d4opt_start( &cb ) ;

  if ( d4unlock( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul" ) ;

  /* Lock an array of records */
  if ( send_com("LOCK 1,3,8", "0" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(1L, 1 ) ;
  ch_base_locked(3L, 1 ) ;

  if ( send_com("LOCK APPEN", "0" ) )
    return 1;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(0L, 0) ;

  if ( send_com("UNLOCK APP", "0" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(0L, 1) ;

  if ( send_com("LOCK FILE", "-1" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(0L, -1) ;

  if ( send_com("UNLOCK FIL", "-1" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked( 0L, 1 ) ;

  if ( send_com("I4LOCK", "" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) ) 
    return 1 ;

  /* Check to make sure the index file is locked. */
#ifndef S4WINDOWS
  cb.lock_attempts = 1 ;
#endif
  if ( i4lock( w1_ptr ) != (int)r4locked  )
    e4severe( e4result, "t4mul:  Index Locking" ) ;
#ifndef S4WINDOWS
  cb.lock_attempts = 1 ;
#endif

  if ( send_com("UNLOCK ALL", "-1" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(0L, 1) ;

  /* Check to make sure the index file is unlocked. */
#ifndef S4WINDOWS
  cb.lock_attempts = 1 ;
#endif
  if ( i4lock( w1_ptr ) != 0 )
    e4severe( e4result, "t4mul:  Index Locking" ) ;
#ifndef S4WINDOWS
  cb.lock_attempts = 1 ;
#endif
  if ( d4unlock( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul" ) ;

  for ( i=1; i<= 40; i++ )
  {
    if ( send_com("APPEND ONE", "1" ) )
      return 1 ;
    if ( int_ret(r++, i ) )
      return 1 ;

    if ( d4refresh( wo_ptr ) != 0 )
      e4severe( e4result, "t4mul" ) ;
    count = d4reccount( wo_ptr ) ;
    if ( count != (long) i )
      e4severe( e4result, "t4mul:  Count" ) ;
  }

  if ( send_com("UNLOCK ALL", "0" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(0L, 1) ;

  if ( d4check( wo_ptr  ) != 0 )
    e4severe( e4result, "t4mul.c " ) ;
  if ( d4unlock_data( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul.c " ) ;

  for ( i=1; i<= 40; i++ )
  {
    if ( d4go( wo_ptr, (long) i ) != 0 )
      e4severe( e4result, "t4mul" ) ;
    if ( f4long(work) != (long) i )
      e4severe( e4result, "t4mul:  Append Check" ) ;
  }
  if ( d4zap( wo_ptr, 1L, d4reccount( wo_ptr ) ) != 0 )
    e4severe( e4result, "t4mul" ) ;
  if ( d4unlock( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul" ) ;

  if ( send_com("D4APPEND", "20" ) )
    return 1 ;
  append_recs(20L ) ;
  if ( int_ret(r++, 0 ) )
    return 1 ;

  if ( d4refresh( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul" ) ;
  if ( d4reccount( wo_ptr ) != 40 )
    e4severe( e4result, "t4mul:  Wrong Count" ) ;

  if ( send_com("LOCK APPEN", "0" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(0L, 0) ;

  if ( send_com("D4LOCK", "5" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(5L, 0 ) ;

  if ( send_com("LOCK APPEN", "0" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(5L, 0) ;

  if ( send_com("D4UNLOCK", "0" ) ) /* mean unlock records only */
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(0L, 0 ) ;

  if ( send_com("D4LOCK", "2" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(2L, 0 ) ;

  if ( send_com("D4UNLOCK", "1" ) ) /* mean unlock records only */
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(0L, 0 ) ;

  if ( send_com("D4LOCK", "5" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(5L, 0 ) ;

  if ( send_com("UNLOCK ALL", "-1" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(0L, 1) ;


  /* Test Dirty Read. */
  if ( send_com("LOCK FILE", "-1" ) )
    return 1 ;
  if ( int_ret(r++, 0 ) )
    return 1 ;
  ch_base_locked(0L, -1) ;

  cb.read_lock = 0 ;
  if ( d4go( wo_ptr, 5L ) != 0 )
    e4severe( e4result, "t4mul:  Dirty Read" ) ;
  cb.read_lock = 1 ;
  if ( f4long(work) != 5L )
    e4severe( e4result, "t4mul:  Dirty Read 2" ) ;

  /* Test Lock Wait. */
#ifndef S4WINDOWS
  cb.lock_attempts = 1 ;
#endif
  if ( d4go( wo_ptr, 5L ) != r4locked )
    e4severe( e4result, "t4mul:  Lock Wait" ) ;
#ifndef S4WINDOWS
  cb.lock_attempts = -1 ;
#endif
  cb.read_lock = 0 ;

  if ( send_com("D4SKIP", "1" ) )
    return 1 ;
  skip_recs(-1L ) ;
  if ( int_ret(r++, 0 ) )
    return 1 ;

  if ( send_com("D4WRITE", "20" ) )  /* Write Records 1 through 4 many times. */
    return 1 ;
  write_recs(-20L ) ;       /* Write Records Backwards. */
  if ( int_ret(r++, 0 ) )
    return 1 ;

  if ( send_com("D4WRITE", "20" ) )  /* Write Records 1 through 4 many times. */
    return 1 ;
  skip_recs(-1L ) ;
  if ( int_ret(r++, 0 ) )
    return 1 ;

  if ( send_com("D4SEEK", "20" ) )
    return 1 ;
  skip_recs(-1L ) ;
  if ( int_ret(r++, 0 ) )
    return 1 ;

  if ( send_com("D4SEEK", "-20" ) )
    return 1 ; 
  write_recs( 20L ) ;
  if ( int_ret(r++, 0 ) )
    return 1 ;

  if ( d4check( wo_ptr ) < 0 )
    e4severe( e4result, "t4mul:  Check 1" ) ;

  if ( send_com("DONE", "" ) )
    return 1 ;

  return 0 ;
}


static int seek_recs( long n )
{
  long i ;
  long on_rec, direc ;

  direc = 1L ;
  if ( n < 0 )
  {
    direc = -1L ;
    n = -n ;
  }

  d4display_str( disp, "        Seek Number Recs: ", 1 ) ;
  d4display_num( disp, n, 0 ) ;
  d4display_str( disp, "        Seeking Record:  ", 1 ) ;

  for ( i = on_rec = 1L; i<= n; i++)
  {
    disp->x = (int) 0 ;
    d4display_str( disp, "        Seeking Record:  ", 0 ) ;
    d4display_num( disp, on_rec, 0 ) ;

    c4ltoa45( on_rec, seek_str, (int) WORK_WIDTH ) ;
    seek_str[WORK_WIDTH] = 0 ;
    if ( d4seek( wo_ptr, seek_str) != 0 )
      e4severe( e4result, "t4mul.c:  d4seek" ) ;

    if ( d4recno( wo_ptr ) != on_rec )
      e4severe( e4result, "t4mul: d4seek_double" ) ;

    on_rec += direc ;
    if ( on_rec < 1L )
      on_rec = d4reccount( wo_ptr ) ;
    if ( on_rec > d4reccount( wo_ptr ))
      on_rec = 1L ;

    if ( d4unlock( wo_ptr ) != 0 )
      e4severe( e4result, "t4mul" ) ;
  }
  return 0 ;
}


static int  write_recs(  long n )
{
  long i ;
  long on_rec, direc ;

  direc = 1L ;
  if ( n < 0 )
  {
    direc = -1L ;
    n = -n ;
  }
  d4display_str( disp, "        ", 1 ) ;

  for ( on_rec = i=1L; i<= n; on_rec += direc, i++)
  {
    if ( on_rec < 1L )  on_rec = 20L ;
    if ( on_rec > 20L ) on_rec = 1L ;

    disp->x = (int) 0 ;
    d4display_str( disp, "        Iteration: ", 0 ) ;
    d4display_num( disp, i, 0 ) ;
    d4display_str( disp, "    Record: ", 0 ) ;
    d4display_num( disp, on_rec, 0 ) ;
    d4display_str( disp, "        ", 1 ) ;

    if ( d4go( wo_ptr,  on_rec ) != 0 )
      e4severe( e4result, "t4mul" ) ;
    f4assign_long( work, on_rec ) ;
    if ( d4flush( wo_ptr ) != 0 )
      e4severe( e4result, (char *) 0 ) ;
    if ( d4unlock( wo_ptr ) != 0 )
      e4severe( e4result, "t4mul" ) ;
  }
  return 0 ;
}


static void  d_append_one()
{
  d4display_str( disp, "Append One Next", 1 ) ;

  if ( d4refresh( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul" ) ;

  if ( d4append_start( wo_ptr, 0 ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  f4assign_long( work, d4reccount(wo_ptr) + 1L) ;
  if ( d4append( wo_ptr ) < 0 )
    e4severe( e4result, "t4mul.c " ) ;

  if ( d4flush( wo_ptr ) != 0 )
    e4severe( e4result, (char *) 0 ) ;
  if ( d4unlock( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul" ) ;

  result_int( (int) d4reccount( wo_ptr ) ) ;
}


static int append_recs(  long n )
{
  long i ;

  d4display_str( disp, "        Appending Total:  ", 1 ) ;
  d4display_num( disp, n, 0 ) ;
  d4display_str( disp, "        Appending:  ", 1 ) ;

  for ( i=1L; i<= n; i++ )
  {
    disp->x = (int) 0 ;
    d4display_str( disp, "        Appending:  ", 0 ) ;
    d4display_num( disp, i, 0 ) ;

    if ( d4append_blank( wo_ptr ) < 0 ) 
      e4severe( e4result, "t4mul.c " ) ;

    f4assign_long( work, d4recno(wo_ptr) ) ;

    if ( d4flush( wo_ptr ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;
    if ( d4unlock( wo_ptr ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;

    if ( t4sleep( 50 ) < 0 )
      return -1 ;
  }

  if ( d4check( wo_ptr  ) != 0 )
    e4severe( e4result, "t4mul.c " ) ;
  if ( d4unlock_data( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul.c " ) ;

  if ( d4unlock( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  return 0 ;
}


static int  skip_recs(  long direc )
{
  d4display_str( disp, "        Skipping Direction:  ", 1 ) ;
  d4display_num( disp, direc, 0 ) ;

  if ( d4refresh( wo_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;
#ifdef S4WINDOWS
  while ( d4lock_file( wo_ptr ) == r4locked  )   /* until free */
    if ( t4sleep(50) < 0 ) return - 1 ;
#endif

  if ( direc < 0 )
  {
    direc = -1L ;
    if ( d4bottom( wo_ptr  ) != 0 )
      e4severe( e4result, "t4mul" ) ;
  }
  else
  {
    direc = 1L ;
    if ( d4top( wo_ptr  ) != 0 )
      e4severe( e4result, "t4mul" ) ;
  }

  d4display_str( disp, "        Record On:  ", 1 ) ;
  for(;;)
  {
    disp->x = (int) 0 ;
    d4display_str( disp, "        Record On:  ", 0 ) ;
    d4display_num( disp, d4recno( wo_ptr ), 0 ) ;

    if ( d4skip( wo_ptr, direc ) < 0 )
      if ( !d4bof( wo_ptr )  && !d4eof( wo_ptr )  )
        e4severe( e4result, "t4mul" ) ;

    if ( f4long(work) != d4recno( wo_ptr ) )
      if ( ! d4eof( wo_ptr )  )
        e4severe( e4result, "t4mul" ) ;

    if ( d4unlock( wo_ptr ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;

    if ( d4bof( wo_ptr )  || d4eof( wo_ptr )  )
    {
      if ( d4recno( wo_ptr ) != 1L && d4recno( wo_ptr ) != d4reccount( wo_ptr )+1L )
        e4severe( e4result, "t4mul" ) ;
      return 0 ;
    }
  }
}


static int result_int( int i )
{
  if ( d4lock_file( re_ptr ) < 0 )
    e4severe( e4result, "t4mul.c " ) ;

  if ( d4append_start( re_ptr, 0 ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  f4assign_int( result, i ) ;

  if ( d4append( re_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  if ( d4flush( re_ptr ) != 0 )
    e4severe( e4result, (char *) 0 ) ;
  if ( d4unlock( re_ptr ) != 0 )
    e4severe( e4result, "t4mul.c" ) ;

  return i ;
}

static void  d_lock()
{
  result_int( d4lock( wo_ptr, f4long(parm) ) ) ;
}

static void d_lock_append()
{
  result_int( d4lock_append( wo_ptr )  ) ;
}

static void d_lock_file()
{
  result_int( d4lock_file( wo_ptr ) ) ;
}

static void d_lock_138()
{
  long a[3] ;
  a[0] = 1L ;
  a[1] = 3L ;
  a[2] = 8L ;
  result_int( d4lock_group( wo_ptr, a, 3) ) ;
}

static void  d_unlock()
{
  result_int( d4unlock_records( wo_ptr ) ) ;
}

static void  d_unlock_append()
{
  result_int( d4unlock_append( wo_ptr )  ) ;
}

static void  d_unlock_all()
{
  result_int( d4unlock( wo_ptr ) ) ;
}

static void  d_unlock_file()
{
  result_int( d4unlock_file( wo_ptr )  ) ;
}

static void  d_seek()
{
  result_int( seek_recs( f4long(parm) )) ;
}

static void  d_skip()
{
  result_int( skip_recs( f4long(parm) )) ;
}

static void  d_write()
{
  result_int( write_recs( f4long(parm) )) ;
}

static void  d_append()
{
  result_int( append_recs( f4long(parm) )) ;
}

static void  i_lock()
{
  result_int( i4lock( w1_ptr )) ;
}

typedef struct  list_st
{
  char work[WORK_WIDTH+1] ;
  void  (*f)() ;
}  LIST ;


LIST list[] = 
{
{ "D4LOCK    ", d_lock },
{ "LOCK APPEN", d_lock_append },
{ "LOCK FILE ", d_lock_file },
{ "LOCK 1,3,8", d_lock_138 },
{ "D4UNLOCK  ", d_unlock },
{ "UNLOCK ALL", d_unlock_all },
{ "UNLOCK FIL", d_unlock_file },
{ "UNLOCK APP", d_unlock_append },
{ "D4SEEK    ", d_seek },
{ "D4SKIP    ", d_skip },
{ "D4WRITE   ", d_write},
{ "D4APPEND  ", d_append},
{ "I4LOCK    ", i_lock},
{ "APPEND ONE", d_append_one},
{ "", 0 },
} ;


static int work_doer()
{
  long co_rec ;
  int i ;

  d4opt_start( &cb ) ;
  d4display_str( disp, "        Received Command: ", 1 ) ;

  for ( co_rec = 1L;;)
  {
    if ( d4display_quit( disp ) )
      return 1 ;

    /* Get a Command */
    if ( d4refresh( co_ptr ) != 0 ) 
      e4severe( e4result, "t4mul.c" ) ;
    if ( d4reccount( co_ptr ) < co_rec )
    {
      if ( t4sleep( 50 ) < 0 )
        return -1 ;
      continue ;
    }

    if ( d4go( co_ptr, co_rec++ ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;
    if ( d4unlock( co_ptr ) != 0 )
      e4severe( e4result, "t4mul.c" ) ;

    disp->x = (int) 0 ;
    d4display_str( disp, "        Received Command: ", 0 ) ;
    d4display_str( disp, f4str(command_field), 1 ) ;
    d4display_str( disp, "    Parameter: ", 0 ) ;
    d4display_str( disp, f4str(parm), 1 ) ;

    for ( i=0;; i++ )
    {
      if ( list[i].work[0] == '\0' )
      {
        if ( memcmp( f4ptr(command_field), "DONE", 4 ) == 0 )
          return 0 ;
        e4severe( e4result, "Unrecognized Command" ) ;
      }
      else
      {
        if ( strcmp( f4str(command_field), list[i].work )  == 0 )
        {
#ifdef __TURBOC__
#pragma warn -pro
#endif

          (*list[i].f)() ;

#ifdef __TURBOC__
#pragma warn .pro
#endif

          break ;
        }
      }
    }
  }
}


static int  test_with_mem_check()
{
  d4init( &cb ) ;

  cb.hWnd = (unsigned) disp->hWnd ;
#ifdef S4DLL
  cb.hInst = (unsigned) disp->hInst ;
#endif

  if ( test_multi() )
    return 1 ;

  d4init_undo( &cb ) ;
  mem4reset() ;

#ifdef S4DEBUG
  mem4check_memory() ;

#ifndef S4DLL
  if ( mem4free_check(100) != 0 )
    e4severe( e4result, "t4mul.c:  Memory items not freed" ) ;
#endif
#endif
  return 0 ;
}

#endif

int S4FUNCTION t4test( D4DISPLAY *display )
{
#ifdef S4INDEX_OFF
  display->y += 2 ;
  d4display_str( display, "T4MUL:   REQUIRES INDEXING (S4INDEX_OFF DEFINED)", 1) ;
  d4display_str( display, "", 1) ;
  return 1 ;
#else
  d4display_str( display, "T4MUL Test  ", 1 ) ;
  d4display_str( display, " ", 1 ) ;

  if ( test_with_mem_check() )
    e4exit( &cb ) ;
  display->y += 2 ;
  d4display_str( display,"T4MUL:   SUCCESS", 1) ;
  d4display_str( display, "", 1) ;
  return 1 ;
#endif
}
