/* log4dbf.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */
/* Convert .LOG file to .DBF format */

/****************************************************************************
**::  Preprocessor Directives
*****************************************************************************/

#include "d4all.h"

#include <ctype.h>
#ifdef S4WINTEL
   #include <direct.h>
#else
   #ifdef S4UNIX
      #include <sys/stat.h>
   #endif
#endif


#ifndef S4UTILS
   #error - Must compile with S4UTILS defined
#endif

#include "u4trans.h"

#ifndef S4WINDOWS
#ifndef S4UNIX
   #ifdef __TURBOC__
      #pragma hdrstop
   #endif
   extern unsigned _stklen = 10000;
#endif
#endif

#ifdef S4DLL_BUILD
   extern FILE *stdout, *stderr ;
   extern unsigned int messageType ;
   HWND hWndStatus ;  /* status window handle */
#endif

#define TIME4LEN      21
#define FILENAMELEN  12

static char ascTime[TIME4LEN+1];

#define UTIL4ADD        0
#define UTIL4ALL        1
#define UTIL4INCLUDE    2
#define UTIL4LOG        3
#define UTIL4MISC       4
#define UTIL4MULTIPLE   5
#define UTIL4NETID      6
#define UTIL4ORIGINAL   7
#define UTIL4OUTDIR     8
#define UTIL4OVERWRITE  9
#define UTIL4REMOVE    10
#define UTIL4TIME      11
#define UTIL4USERID    12
#ifdef S4UTIL_CONCURRENCY
   #define UTIL4CONFIG 13
#endif

/****************************************************************************
**::  Global scope types and variables
*****************************************************************************/

static UTIL4FLAG u4flags[] =
{
   { "ADd",       0, 2, 2,  3, 0, 0, 0, 0, "[-add <data.dbf> [<original.dbf>] [<output.chg>]]\r\n" },
   { "ALl",       0, 2, 1,  1, 1, 0, 0, 0, "[-all {OFF | ON}]\r\n"            },
   { "Include",   0, 1, 2,  6, 0, 0, 0, 0, "[-include [ALL] [INITIAL] [MEMO] [OPEN] [ROLLBACK] [TEMP] [UPDATE]]\r\n" },
   { "Log",       0, 1, 1,  1, 1, 0, 0, 0, "{-log <server.log>}\r\n"          },
   { "MIsclen",   0, 2, 1,  1, 1, 0, 0, 0, "[-misclen <#>]\r\n"               },
   { "MUltiple",  0, 2, 1,  1, 1, 0, 0, 0, "[-multiple {OFF | ON}]\r\n"       },
   { "Netidlen",  0, 1, 1,  1, 1, 0, 0, 0, "[-netidlen <#>]\r\n"              },
   { "ORiginal",  0, 2, 1,  1, 1, 0, 0, 0, "[-original {OFF | ON}]\r\n"       },
   { "OUtdir",    0, 2, 1,  1, 1, 0, 0, 0, "[-outdir <output directory>]\r\n" },
   { "OVerwrite", 0, 2, 1,  1, 1, 0, 0, 0, "[-overwrite {OFF | ON}]\r\n"      },
   { "Remove",    0, 1, 2,  1, 1, 0, 0, 0, "[-remove <data.dbf>]\r\n"         },
   { "Time",      0, 1, 1,  1, 1, 0, 0, 0, "[-time {OFF | ON}]\r\n"           },
   { "Useridlen", 0, 1, 1,  1, 1, 0, 0, 0, "[-useridlen <#>]\r\n"             },
#ifdef S4UTIL_CONCURRENCY
   { "Config",    0, 1, 1, -1, 0, 0, 0, 0, "{-config [<config.dbf>]}\r\n"     },
#endif
};

#ifdef S4UTIL_CONCURRENCY
   static int u4flagsNum = 14;
#else
   static int u4flagsNum = 13;
#endif

#define INCLUDE_UPDATE   1<<0
#define INCLUDE_INITIAL  1<<1
#define INCLUDE_ROLLBACK 1<<2
#define INCLUDE_TEMP     1<<3
#define INCLUDE_OPEN     1<<4
#define INCLUDE_MEMO     1<<5
#define INCLUDE_ALL      (1<<6)-1

typedef struct SETTINGSTRUCT_st
{
   #ifdef S4UTIL_CONCURRENCY
      char *config;
   #endif
   char *log;
   char *outDir;
   char **addDbf; /* actually "remove" Dbf's if all == 1 */
   char numAdd;
   char all;
   char include;
   char multiple;
   char original;
   char overwrite;
   char time;
   short netid;
   short userid;
   short misc;
} SETTINGSTRUCT;

static FIELD4INFO openFields[] =
{
   { "NETNAME",    'C', 80,       0 },
   { "USERNAME",   'C', 80,       0 },
   { "CLIENTID",   'N', 11,       0 },
   { "SERVERDTID", 'N', 11,       0 },
   { "RECORDWID",  'N',  5,       0 },
   { "TRANID",     'N', 11,       0 },
   { "DATA4PTR",   'N', 11,       0 },
   { "FILENAME",   'C', LEN4PATH, 0 },
   { "TEMP",       'L',  1,       0 },
   { 0,0,0,0 },
};

static TAG4INFO openTags[] =
{
   { "CLID", "STR(CLIENTID,11,0)+STR(SERVERDTID,11,0)", ".NOT.DELETED()", r4unique, 0 },
   { "TRANID", "STR(TRANID,11,0)", ".NOT.DELETED()", 0, 0 },
   { 0,0,0,0,0 },
};

#define L4TYPE_F     0
#define L4ID_F       1
#define L4TIME_F     2
#define L4NETID_F    3
#define L4USERID_F   4
#define L4ROLLED_F   5
#define L4MISC_F     6
#define L4RECNO_F    7
#define L4DELETED_F  8
#define L4RECORD_F   9
#define L4_F        10

static FIELD4INFO outFieldsTemp[] =
{
   { "L4TYPE",    'C', 10, 0 },
   { "L4ID",      'N', 11, 0 },
   { "L4TIME",    'C', 21, 0 },
   { "L4NETID",   'C',  0, 0 },
   { "L4USERID",  'C',  0, 0 },
   { "L4ROLLED",  'L',  1, 0 },
   { "L4MISC",    'C',  0, 0 },
   { "L4RECNO",   'N',  9, 0 },
   { "L4DELETED", 'L',  1, 0 },
   { "L4RECORD",  'C',  0, 0 },
   { 0, 0, 0, 0 },
};

static FIELD4INFO tranFields[] =
{
   { "TRANID", 'N', 11, 0 },  /* rolledback transaction ids */
   { 0,0,0,0 },
};

static TAG4INFO tranTags[] =
{
   { "TRANID_T", "STR(TRANID,11,0)", ".NOT.DELETED()", r4unique, 0 },
   { 0,0,0,0,0 },
};

#ifndef S4WINDOWS
   char *statusStringBlank = "                         ";
   char *statusStringMessg = "Processing log file: ";
   char *statusStringBckSp = "\r";
   char *statusStringFormt = "%3d.%02d%%%%";
   #ifdef S4UNIX
      char statusStringSpace[10];
   #else
      char *statusStringSpace = "100.00  ";
   #endif

#else
   extern char *statusStringBlank ;
   extern char *statusStringMessg ;
   extern char *statusStringBckSp ;
   extern char *statusStringFormt ;
   extern char *statusStringSpace ;
#endif

/*::*************************************************************************

*****************************************************************************
**::  Function: checkFlags
**
*****************************************************************************
**
**    Function: checkFlags()
**
****************************************************************************/
int checkFlags(CODE4 *c4, UTIL4FLAG u4[], int numFlags, SETTINGSTRUCT *settings)
{
   char *outMess[5];
   unsigned i;

   settings->all = util4checkOnOffCommandLine(c4, u4flags, UTIL4ALL, 0);
   if (settings->all == -1)
      return(-1);

   settings->multiple = util4checkOnOffCommandLine(c4, u4flags, UTIL4MULTIPLE, 0);
   if (settings->multiple == -1)
      return(-1);

   settings->original = util4checkOnOffCommandLine(c4, u4flags, UTIL4ORIGINAL, 1);
   if (settings->original == -1)
      return(-1);

   settings->overwrite = util4checkOnOffCommandLine(c4, u4flags, UTIL4OVERWRITE, 0);
   if (settings->overwrite == -1)
      return(-1);

   settings->time = util4checkOnOffCommandLine(c4, u4flags, UTIL4TIME, 1);
   if (settings->time == -1)
      return(-1);

   settings->netid = (int)util4checkRangeCommandLine(c4, u4flags, UTIL4NETID, 20, 0, 80);
   if (settings->netid == -1)
      return(-1);

   settings->userid = (int)util4checkRangeCommandLine(c4, u4flags, UTIL4USERID, 20, 0, 80);
   if (settings->userid == -1)
      return(-1);

   settings->misc = (int)util4checkRangeCommandLine(c4, u4flags, UTIL4MISC, 0, 0, 30000);
   if (settings->misc == -1)
      return(-1);

   #ifdef S4UTIL_CONCURRENCY
      if (u4flags[UTIL4CONFIG].ptr != 0)
         settings->config = u4flags[UTIL4CONFIG].ptr[0];
      else
         if (u4flags[UTIL4CONFIG].found)
            settings->config = "";
   #endif

   #ifndef S4UTIL_CONCURRENCY
      if ( u4flags[UTIL4LOG].found == 0 )
      {
         outMess[0] = "Command line error! Must specify -log switch.\r\n\r\n";
         util4out(c4, stdout, outMess, 1);
         return(-1);
      }
   #endif
   if (u4flags[UTIL4LOG].ptr != 0)
      settings->log = u4flags[UTIL4LOG].ptr[0];

   if (u4flags[UTIL4OUTDIR].ptr != 0)
      settings->outDir = u4flags[UTIL4OUTDIR].ptr[0];
   else
      settings->outDir = "LOG4DBF";
   if (settings->all == 0)
   {
      settings->addDbf = u4flags[UTIL4ADD].ptr;
      settings->numAdd = u4flags[UTIL4ADD].numPtr/4;
   }
   else
   {
      settings->addDbf = u4flags[UTIL4REMOVE].ptr;
      settings->numAdd = u4flags[UTIL4REMOVE].numPtr/4;
   }

   settings->include = 0;
   for (i=0; i<u4flags[UTIL4INCLUDE].numPtr/4; i++)
   {
      c4upper ( u4flags[UTIL4INCLUDE].ptr[i]+1 ) ;
      if (memcmp(u4flags[UTIL4INCLUDE].ptr[i]+1, "ALL", 3) == 0)
      {
         settings->include = INCLUDE_ALL;
         continue;
      }
      if (memcmp(u4flags[UTIL4INCLUDE].ptr[i]+1, "UPDATE", 6) == 0)
      {
         settings->include |= INCLUDE_UPDATE;
         continue;
      }
      if (memcmp(u4flags[UTIL4INCLUDE].ptr[i]+1, "INITIAL", 7) == 0)
      {
         settings->include |= INCLUDE_INITIAL;
         continue;
      }
      if (memcmp(u4flags[UTIL4INCLUDE].ptr[i]+1, "ROLLBACK", 8) == 0)
      {
         settings->include |= INCLUDE_ROLLBACK;
         continue;
      }
      if (memcmp(u4flags[UTIL4INCLUDE].ptr[i]+1, "TEMP", 4) == 0)
      {
         settings->include |= INCLUDE_TEMP;
         continue;
      }
      if (memcmp(u4flags[UTIL4INCLUDE].ptr[i]+1, "OPEN", 4) == 0)
      {
         settings->include |= INCLUDE_OPEN;
         continue;
      }
      if (memcmp(u4flags[UTIL4INCLUDE].ptr[i]+1, "MEMO", 4) == 0)
      {
         settings->include |= INCLUDE_MEMO;
         continue;
      }
      outMess[0] = "Command line error! -";
      outMess[1] = u4flags[UTIL4INCLUDE].name;
      outMess[2] = " flag has unknown value: ";
      outMess[3] = u4flags[UTIL4INCLUDE].ptr[i]+1;
      outMess[4] = "\r\n";
      util4out(c4, stdout, outMess, 5);
      return(-1);
   }
   if (i == 0)
      settings->include = INCLUDE_UPDATE;  /* default */
   return(0);
}

/****************************************************************************
**::  Function: getLogFileName
**
*****************************************************************************
**
**    Function: getLogFileName()
**
****************************************************************************/
static int getLogFileName(CODE4 *c4, SETTINGSTRUCT *settings, char **logFileName)
{
   #ifdef S4UTIL_CONCURRENCY
      int rc=-1;
      DATA4 *configDbf;
      char *outMess[1];
   #endif

   if (settings->log != 0)
   {
      *logFileName = (char *)u4allocFree(c4, strlen(settings->log)+1);
      if (*logFileName == 0)
         return(-1);
      strcpy(*logFileName, settings->log);
      return(0);
   }
   #ifndef S4UTIL_CONCURRENCY
   else
      return(-1) ;
   #endif

   #ifdef S4UTIL_CONCURRENCY
      if (settings->config != 0)
      {
         if (*settings->config == 0)
            configDbf = d4open(c4, "S4SERVER");
         else
            configDbf = d4open(c4, settings->config);
         if (configDbf == 0)
            return(-1);
         rc = d4top(configDbf);
         if (rc == 0)
         {
            *logFileName = u4allocFree(c4, f4len(d4field(configDbf, "LOGNAME"))+1);
            if (*logFileName == 0)
               rc = -1;
         }
         if (rc == 0)
         {
            strcpy(*logFileName, f4str(d4field(configDbf, "LOGNAME")));
            c4trimN(*logFileName, f4len(d4field(configDbf, "LOGNAME"))+1);
            if (**logFileName == 0)
            {
               rc = -1;
               outMess[0] = "Config Dbf error! Logging is disabled.\r\n";
               util4out(c4, stdout, outMess, 1);
            }
         }
         d4close(configDbf);
      }
      return(rc);
   #endif
}

/****************************************************************************
**::  Function: typeTran4Shutdown
**
*****************************************************************************
**
**    Function: typeTran4Shutdown()
**
****************************************************************************/
short typeTran4Shutdown(CODE4 *c4, DATA4 *openDbfs, TRAN4 *t4)
{
   short rc;
   char *outMess[1];

   rc = d4top(openDbfs);
   if (rc != r4eof && rc != 0)
      return(rc);
   if (rc == 0)
   {
      outMess[0] = "\r\n\r\nLog file error! Server shutdown while user(s) initialized.\r\n\r\n";
      util4out(c4, stdout, outMess, 1);
      return(-1);
   }
   if (tran4serverDataId(t4) > TRAN4VERSION_NUM)
   {
      outMess[0] = "\r\n\r\nLog file error! File is a newer version than this utility can handle!\r\n\r\n";
      util4out(c4, stdout, outMess, 1);
      return(-1);
   }
   return(0);
}

/****************************************************************************
*::  Function: typeTran4Init
**
*****************************************************************************
**
**    Function: typeTran4Init()
**
****************************************************************************/
short typeTran4Init(CODE4 *c4, DATA4 *openDbfs, TRAN4 *t4)

{
   short rc;
   char *data, *outMess[1];

   rc = d4appendStart(openDbfs, 0);
   if (rc != 0)
      return(rc);
   d4blank(openDbfs);
   data = (char *)tran4getData(t4, 0L);
   if (data == 0)
      return(-1);
   f4assignN(d4field(openDbfs, "NETNAME"), data+2, *(short *)data);
   f4assignN(d4field(openDbfs, "USERNAME"), data+4+ *(short *)data, *(short *)(data+2+ *(short *)data));
   f4assignLong(d4field(openDbfs, "CLIENTID"), tran4clientId(t4));
   rc = d4append(openDbfs);
   if (rc == r4unique)
   {
      outMess[0] = "\r\n\r\nLog file error! User already initialized.\r\n\r\n";
      util4out(c4, stdout, outMess, 1);
   }
   return(rc);
}

/****************************************************************************
*::  Function: typeTran4InitUndo
**
*****************************************************************************
**
**    Function: typeTran4InitUndo()
**
****************************************************************************/
short typeTran4InitUndo(CODE4 *c4, DATA4 *openDbfs, TRAN4 *t4)
{
   short rc;
   char client[12], *outMess[1];

   util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
   d4tagSelect(openDbfs, d4tag(openDbfs, "CLID"));
   rc = d4seek(openDbfs, client);
   if (rc != 0)
   {
      outMess[0] = "\r\n\r\nLog file error! User not initialized.\r\n\r\n";
      util4out(c4, stdout, outMess, 1);
      return(rc);
   }
   d4delete(openDbfs);
   rc = d4seek(openDbfs, client);
   if (rc != r4after && rc != r4eof)
   {
      if (rc == 0)
      {
         outMess[0] = "\r\n\r\nLog file error! User uninitialized with DBF's still open.\r\n\r\n";
         util4out(c4, stdout, outMess, 1);
         return(-1);
      }
      return(rc);
   }
   return(0);
}

/****************************************************************************
**::  Function: openOriginal
**
*****************************************************************************
**
**    Function: openOriginal()
**
****************************************************************************/
short openOriginal(CODE4 *c4, DATA4** origDbf, char *fileName, long logRecordWidth, short logNumFields)
{
   short rc=0;

   c4->errOpen = 0;
   *origDbf = d4open(c4, fileName);
   c4->errOpen = 1;
   if (!*origDbf)
      if (c4->errorCode != r4noOpen)
         return(-1);
      else
         c4->errorCode = 0;
   else
      if ((unsigned)logRecordWidth != d4recWidth(*origDbf) || logNumFields != d4numFields(*origDbf))
      {
         rc = d4close(*origDbf);
         *origDbf = 0;
      }
   return(rc);
}

/****************************************************************************
**::  Function: noPath
**
*****************************************************************************
**
**    Function: noPath()
**
****************************************************************************/
char *noPath(char *fullName)
{
   short i=-1;

   if (!fullName)
      return(fullName);
   while (fullName[++i] != 0);
   while (--i >= 0 && fullName[i] != '\\' && fullName[i] != ':');
   return(fullName+i+1);
}

/****************************************************************************
**::  Function: getNextFileName
**
*****************************************************************************
**
**    Function: getNextFileName()
**
****************************************************************************/
short getNextFileName(char *fileName)
{
   short i=-1, j, k, l;

   while (fileName[++i] != 0);
   j = i;
   while (--i >= 0 && fileName[i] != '.');
   if (i == -1)
      i = j;
   if (i > 3 && isdigit(fileName[i-3]) && isdigit(fileName[i-2]) && isdigit(fileName[i-1]))
   {
      for (k=1; k<4; k++)
      {
         fileName[i-k]++;
         if (fileName[i-k] == ':')
            fileName[i-k] = '0';
         else
            break;
      }
      if (k == 4)
         return(-1);
   }
   else
   {
      if (i >= FILENAMELEN-7)
      {
         k = FILENAMELEN - 7;
         for (l=j-1; l>=i; l--)
            fileName[l+FILENAMELEN-i-4] = fileName[l];
         fileName[j+FILENAMELEN-i-4] = 0;
      }
      else
      {
         k = i;
         for (l=j-1; l>=i; l--)
            fileName[l+3] = fileName[l];
         fileName[j+3] = 0;
      }
      fileName[k] = fileName[k+1] = fileName[k+2] = '0';
   }
   return(0);
}

/****************************************************************************
**::  Function: checkField
**
*****************************************************************************
**
**    Function: checkField()
**
****************************************************************************/
short checkField(FIELD4 *f4, FIELD4INFO *info, unsigned len)
{
   if (strcmp(info->name, f4name(f4)) != 0)
      return(-1);
   if (len != 0)
   {
      if (len != f4len(f4))
         return(-1);
   }
   else
      if (info->len != f4len(f4))
         return(-1);
   if (info->type != f4type(f4))
      return(-1);
   if ((int)info->dec != f4decimals(f4))
      return(-1);
   return(0);
}

/****************************************************************************
**::  Function: checkStructure
**
*****************************************************************************
**
**    Function: checkStructure()
**
****************************************************************************/
short checkStructure(SETTINGSTRUCT *settings, DATA4 *origDbf, DATA4 *tempDbf, long logRecordWidth, long *tempNumFields)
{
   short rc=0, count=2, numFields, j;
   long tempLength=0;

   if (tempDbf)
   {
      numFields = d4numFields(tempDbf);
      if (numFields < 4)
         rc = -1;
   }
   tempLength = outFieldsTemp[L4TYPE_F].len;
   if (!tempDbf || rc != 0 || numFields-- <= 0 || checkField(d4fieldJ(tempDbf, 1), outFieldsTemp+L4TYPE_F, 0) != 0)
      rc = -1;
   tempLength += outFieldsTemp[L4ID_F].len;
   if (!tempDbf || rc != 0 || numFields-- <= 0 || checkField(d4fieldJ(tempDbf, 2), outFieldsTemp+L4ID_F, 0) != 0)
      rc = -1;
   if (settings->time)
   {
      tempLength += outFieldsTemp[L4TIME_F].len;
      count++;
      if (!tempDbf || rc != 0 || numFields-- <= 0 || checkField(d4fieldJ(tempDbf, count), outFieldsTemp+L4TIME_F, 0) != 0)
         rc = -1;
   }
   if (settings->netid > 0)
   {
      tempLength += settings->netid;
      count++;
      if (!tempDbf || rc != 0 || numFields-- <= 0 || checkField(d4fieldJ(tempDbf, count), outFieldsTemp+L4NETID_F, settings->netid) != 0)
         rc = -1;
   }
   if (settings->userid > 0)
   {
      tempLength += settings->userid;
      count++;
      if (!tempDbf || rc != 0 || numFields-- <= 0 || checkField(d4fieldJ(tempDbf, count), outFieldsTemp+L4USERID_F, settings->userid) != 0)
         rc = -1;
   }
   if (settings->include & INCLUDE_ROLLBACK)
   {
      tempLength += outFieldsTemp[L4ROLLED_F].len;
      count++;
      if (!tempDbf || rc != 0 || numFields-- <= 0 || checkField(d4fieldJ(tempDbf, count), outFieldsTemp+L4ROLLED_F, 0) != 0)
         rc = -1;
   }
   if (settings->misc > 0)
   {
      tempLength += settings->misc;
      count++;
      if (!tempDbf || rc != 0 || numFields-- <= 0 || checkField(d4fieldJ(tempDbf, count), outFieldsTemp+L4MISC_F, settings->misc) != 0)
         rc = -1;
   }
   tempLength += outFieldsTemp[L4RECNO_F].len;
   count++;
   if (!tempDbf || rc != 0 || numFields-- <= 0 || checkField(d4fieldJ(tempDbf, count), outFieldsTemp+L4RECNO_F, 0) != 0)
      rc = -1;
   tempLength += outFieldsTemp[L4DELETED_F].len;
   count++;
   if (!tempDbf || rc != 0 || numFields-- <= 0 || checkField(d4fieldJ(tempDbf, count), outFieldsTemp+L4DELETED_F, 0) != 0)
      rc = -1;

   if (origDbf)
      for (j=1; j<=d4numFields(origDbf); j++)
      {
         if (!(settings->include & INCLUDE_MEMO) && f4type(d4fieldJ(origDbf, j)) == r4memo)
            continue;
         if (count == 2046 || tempLength + f4len(d4fieldJ(origDbf, j)) > 65535)
            break;
         tempLength += f4len(d4fieldJ(origDbf, j));
         count++;
         if (!tempDbf || rc != 0 || numFields-- <= 0 ||
             strcmp(f4name(d4fieldJ(origDbf, j)), f4name(d4fieldJ(tempDbf, count))) != 0 ||
             f4len(d4fieldJ(origDbf, j)) != f4len(d4fieldJ(tempDbf, count)) ||
             f4type(d4fieldJ(origDbf, j)) != f4type(d4fieldJ(tempDbf, count)) ||
             f4decimals(d4fieldJ(origDbf, j)) != f4decimals(d4fieldJ(tempDbf, count)))
            rc = -1;
      }
   else
   {
      count++;
      if (!tempDbf || rc != 0 || numFields != 1 || checkField(d4fieldJ(tempDbf, count), outFieldsTemp+L4RECORD_F, (unsigned)(logRecordWidth > 32767 ? 32767 : logRecordWidth)) != 0)
         rc = -1;
   }
   *tempNumFields = count;
   return(rc);
}

/****************************************************************************
**::  Function: timeformat
**
*****************************************************************************
**
**    Function: timeformat()
**
****************************************************************************/
char *timeformat(TRAN4 *t4)
{
   struct tm *time;

   time = localtime(&t4->header.time);
   #ifndef S4DLL_BUILD
      if (strftime(ascTime, TIME4LEN+1, "%a %m/%d/%y %H:%M:%S", time) == 0)
   #else
      if( sprintf( ascTime, "%.3s %2d/%2d/%2d %.8s", asctime(time), time->tm_mon,
                    time->tm_mday, time->tm_year, asctime(time)+11 ) == EOF )
   #endif
         return(0);
   return(ascTime);
}

/****************************************************************************
**::  Function: typeTran4Open
**
*****************************************************************************
**
**    Function: typeTran4Open()
**
****************************************************************************/
static short typeTran4Open(CODE4 *c4, SETTINGSTRUCT *settings, DATA4 *openDbfs, TRAN4 *t4)
{
   DATA4 *origDbf=0, *tempDbf=0;
   FIELD4INFO *changeFields, *origFields;
   short rc, found, i, j, logNumFields;
   char *data, *fullName, fileName[FILENAMELEN+1], addName[LEN4PATH+1], *originalName=0, *changeName=0, *cmpName;
   char *outName, *outNameMemo, *curDir, *outMess[6];
   long tempNumFields, logRecordWidth;
   char clientId[12];
   static didProcess = 0;
   #ifndef S4UNIX
      unsigned origDrive=0, curDrive, dummy;
   #endif

   if (tran4type(t4) == TRAN4OPEN_TEMP && !(settings->include & INCLUDE_TEMP))
      return(0);

   data = (char *)tran4getData(t4, 0L);
   if (data == 0)
      return(-1);
   fullName = (char *)u4allocFree(c4, *(short *)data+1);
   if (fullName == 0)
      return(-1);
   strncpy(fullName, data+2, *(short *)data);
   fullName[*(short *)data] = 0;

   if (!settings->all)
   {
      found = 0;
      for (i=0; i<settings->numAdd; i++)
      {
         u4namePiece(fileName, FILENAMELEN+1, settings->addDbf[i]+1, 0, 1);
         if (strlen(fileName) != strlen(settings->addDbf[i]+1))
         {
            u4nameCurrent(addName, LEN4PATH+1, settings->addDbf[i]+1);
            #ifdef S4UNIX
               u4nameExt(addName, LEN4PATH+1, ".dbf", 0);
            #else
               u4nameExt(addName, LEN4PATH+1, ".DBF", 0);
            #endif
            cmpName = fullName;
         }
         else
         {
            u4namePiece(addName, LEN4PATH+1, fullName, 0, 1);
            #ifdef S4UNIX
               u4nameExt(fileName, LEN4PATH+1, ".dbf", 0);
            #else
               u4nameExt(fileName, LEN4PATH+1, ".DBF", 0);
            #endif
            cmpName = fileName;
         }
         if (i != settings->numAdd-1 && *settings->addDbf[i] == *settings->addDbf[i+1])
            originalName = settings->addDbf[++i]+1;
         if (i != settings->numAdd-1 && *settings->addDbf[i] == *settings->addDbf[i+1])
            changeName = settings->addDbf[++i]+1;
         if (strcmp(addName, cmpName) == 0)
         {
            found = 1;
            break;
         }
         originalName = changeName = 0;
      }
   }
   else
   {
      found = 1;
      for (i=0; i<settings->numAdd; i++)
      {
         u4namePiece(fileName, FILENAMELEN+1, settings->addDbf[i], 0, 1);
         if (strlen(fileName) != strlen(settings->addDbf[i]))
         {
            u4nameCurrent(addName, LEN4PATH+1, settings->addDbf[i]);
            #ifdef S4UNIX
               u4nameExt(addName, LEN4PATH+1, ".dbf", 0);
            #else
               u4nameExt(addName, LEN4PATH+1, ".DBF", 0);
            #endif
            cmpName = fullName;
         }
         else
         {
            u4namePiece(addName, LEN4PATH+1, fullName, 0, 1);
            #ifdef S4UNIX
               u4nameExt(fileName, LEN4PATH+1, ".dbf", 0);
            #else
               u4nameExt(fileName, LEN4PATH+1, ".DBF", 0);
            #endif
            cmpName = fileName;
         }
         if (strcmp(addName, cmpName) == 0)
         {
            found = 0;
            break;
         }
      }
   }
   strcpy(addName, fullName);
   u4free(fullName);
   if (found == 0)
      return(0);

   #ifdef S4DATA_ALIGN
      memcpy(&logRecordWidth, (data+2+*(short *)data), sizeof(long) );
      memcpy(&logNumFields, (data+6+*(short *)data), sizeof(short) );
   #else
      logRecordWidth = *(long *)(data+2+ *(short *)data);
      logNumFields = *(short *)(data+6+ *(short *)data);
   #endif

   if (originalName)
      rc = openOriginal(c4, &origDbf, originalName, logRecordWidth, logNumFields);
   else
      rc = openOriginal(c4, &origDbf, addName, logRecordWidth, logNumFields);
   if (rc != 0)
      return(rc);

   if (!origDbf && settings->original)
   {
      outMess[0] = "\r\n\r\nData file structure not available! Skipping data file:\r\n";
      outMess[1] = addName;
      #ifndef S4WINDOWS
         outMess[2] = "\r\n\r\n";
         outMess[3] = statusStringBlank;
         util4out(c4, stdout, outMess, 4);
      #else
         util4out(c4, stdout, outMess, 2);
      #endif
      return(0);
   }

   outName = (char *)u4allocFree(c4, LEN4PATH+1);
   if (!outName)
   {
      if (origDbf)
         d4close(origDbf);
      return(-1);
   }
   if (changeName)
      strcpy(outName, changeName);
   else
      if (originalName)
         u4namePiece(outName, LEN4PATH+1, originalName, 0, 1);
      else
         u4namePiece(outName, LEN4PATH+1, addName, 0, 1);
   #ifndef S4UNIX
      #ifndef S4OS2
      if (strlen(settings->outDir) > 1 && *(settings->outDir+1) == ':')
      {
         _dos_getdrive(&origDrive);
         _dos_setdrive(*settings->outDir-'a'<0 ? *settings->outDir-'A'+1 : *settings->outDir-'a'+1, &dummy);
         _dos_getdrive(&curDrive);
         if ((char)curDrive != (*settings->outDir-'a'<0 ? *settings->outDir-'A'+1 : *settings->outDir-'a'+1))
            rc = -1;
      }
      #endif
   #endif
   if (rc == 0)
   {
      curDir = (char *)u4allocFree(c4, LEN4PATH+1);
      if (!curDir)
         rc = -1;
      else
         if( getcwd(curDir, LEN4PATH+1) == NULL)
            rc = -1 ;
   }
   if (rc == 0)
   {
      rc = chdir(settings->outDir);
      if (rc != 0)
      {
         #ifdef S4UNIX
            rc = mkdir(settings->outDir, 0755);
         #else
            rc = mkdir(settings->outDir);
         #endif
         if (rc != 0)
         {
            outMess[0] = "\r\n\r\nAccess error! Can not create output directory:\r\n";
            outMess[1] = settings->outDir;
            outMess[2] = "\r\n\r\n";
            util4out(c4, stdout, outMess, 3);
            #ifndef S4UNIX
               #ifndef S4OS2
               if (origDrive)
                  _dos_setdrive(origDrive, &dummy);
               #endif
            #endif
            u4free(outName);
            if (origDbf)
               d4close(origDbf);
            return(-1);
         }
         rc = chdir(settings->outDir);
      }
   }
   if (rc != 0)
   {
      if (curDir)
         u4free(curDir);
      #ifndef S4UNIX
         #ifndef S4OS2
         if (origDrive)
            _dos_setdrive(origDrive, &dummy);
         #endif
      #endif
      u4free(outName);
      if (origDbf)
         d4close(origDbf);
      return(-1);
   }

   c4->errOpen = 0;
   tempDbf = d4open(c4, outName);
   c4->errOpen = 1;

   if (!tempDbf && c4->errorCode == r4noOpen)
   {
      if( access(outName, 00) == 0 )   /* if file exists, this is an error */
      {
         #ifndef S4UNIX
            #ifndef S4OS2
            if (origDrive)
               _dos_setdrive(origDrive, &dummy);
            #endif
         #endif

         chdir(curDir);
         u4free(curDir);

         if (origDbf)
            d4close(origDbf);

         if (settings->overwrite)
            outMess[0] = "\r\n\r\nAccess error! Can not over-write existing version of output file:\r\n";
         else
            outMess[0] = "\r\n\r\nAccess error! Can not open existing version of output file:\r\n";

         #ifndef S4UNIX
            outMess[1] = strupr(settings->outDir);
            outMess[2] = "\\" ;
            outMess[3] = strupr(outName) ;
         #else
            outMess[1] = settings->outDir;
            outMess[2] = "/" ;
            outMess[3] = outName ;
         #endif
         outMess[4] = "\r\nThe file may be in use by another application.";

         if( didProcess )
         {
            didProcess = 0;
            outMess[5] = "\r\n\r\nNOTE:\nSome analysis output files were successfully created before the"
                         "\r\nerror occurred. Check the date/time stamps on the files located in"
                         "\r\nthe specified output directory."
                         "\r\n\r\n";
         }
         else
            outMess[5] = "\r\n\r\n";

         util4out(c4, stdout, outMess, 6);

         u4free(outName);

         return(-1);
      }
      else
      {
         checkStructure(settings, origDbf, origDbf, logRecordWidth, &tempNumFields);
      }
   }

   for (; tempDbf; )
   {
      if (tempDbf->dataFile->userCount == 1 && settings->overwrite == 1)
      {
         rc = d4close(tempDbf);
         tempDbf = 0;
         if (rc == 0)
         {
            #ifdef S4UNIX
               u4nameExt(outName, LEN4PATH+1, "dbf", 0);
            #else
               u4nameExt(outName, LEN4PATH+1, "DBF", 0);
            #endif
            rc = remove(outName);
            if (rc == 0)
            {
               outNameMemo = (char *)u4allocFree(c4, strlen(outName)+4);
               if (outNameMemo == 0)
                  rc = -1;
               else
               {
                  strcpy(outNameMemo, outName);
                  #ifdef S4MFOX
                     #ifdef S4UNIX
                        u4nameExt(outNameMemo, strlen(outName)+4, "fpt", 1);
                     #else
                        u4nameExt(outNameMemo, strlen(outName)+4, "FPT", 1);
                     #endif
                  #else
                     #ifdef S4UNIX
                        u4nameExt(outNameMemo, strlen(outName)+4, "dbt", 1);
                     #else
                        u4nameExt(outNameMemo, strlen(outName)+4, "DBT", 1);
                     #endif
                  #endif
                  if (access(outNameMemo, 0) == 0)
                     rc = remove(outNameMemo);
                  u4free(outNameMemo);
                  checkStructure(settings, origDbf, origDbf, logRecordWidth, &tempNumFields);
               }
            }
         }
         break;
      }
      rc = checkStructure(settings, origDbf, tempDbf, logRecordWidth, &tempNumFields);
      if (rc == 0)
         break;
      for(;;)
      {
         if (rc != 0 && !settings->multiple)
         {
            rc = d4close(tempDbf);
            rc = rc || chdir(curDir);
            u4free(curDir);
            #ifndef S4UNIX
               #ifndef S4OS2
               if (origDrive)
                  _dos_setdrive(origDrive, &dummy);
               _dos_getdrive(&curDrive);
               if (curDrive != origDrive)
                  rc = -1;
               #endif
            #endif
            u4free(outName);
            if (origDbf)
               rc = rc || d4close(origDbf);
            if (rc != 0)
               return(rc);
            outMess[0] = "\r\n\r\nOutput file does not match log entry description! Skipping file:\r\n";
            outMess[1] = addName;
            #ifndef S4WINDOWS
               outMess[2] = "\r\n\r\n";
               outMess[3] = statusStringBlank;
               util4out(c4, stdout, outMess, 4);
            #else
               util4out(c4, stdout, outMess, 2);
            #endif
            return(0);
         }
         rc = d4close(tempDbf);
         tempDbf = 0;
         if (rc != 0)
            break;
         rc = getNextFileName(noPath(outName));
         if (rc != 0)
         {
            outMess[0] = "\r\n\r\nFile name error! Can not create unique name for output file for:\r\n";
            outMess[1] = addName;
            outMess[2] = "\r\n\r\n";
            util4out(c4, stdout, outMess, 3);
            break;
         }
         c4->errOpen = 0;
         tempDbf = d4open(c4, outName);
         c4->errOpen = 1;
         if (tempDbf)
            rc = checkStructure(settings, origDbf, tempDbf, logRecordWidth, &tempNumFields);
         if (rc == 0)
            break;
      }
      break;
   }
   if (rc != 0 || (!tempDbf && c4->errorCode != r4noOpen && c4->errorCode != 0))
   {
      chdir(curDir);
      u4free(curDir);
      #ifndef S4UNIX
         #ifndef S4OS2
         if (origDrive)
            _dos_setdrive(origDrive, &dummy);
         #endif
      #endif
      u4free(outName);
      if (origDbf)
         d4close(origDbf);
      return(-1);
   }
   c4->errorCode = 0;

   if (!tempDbf)
   {
      changeFields = (FIELD4INFO *)u4allocFree(c4, (tempNumFields+1)*sizeof(FIELD4INFO));
      if (!changeFields)
      {
         chdir(curDir);
         u4free(curDir);
         #ifndef S4UNIX
            #ifndef S4OS2
            if (origDrive)
               _dos_setdrive(origDrive, &dummy);
            #endif
         #endif
         u4free(outName);
         if (origDbf)
            d4close(origDbf);
         return(-1);
      }
      i = 0;
      memcpy(changeFields, outFieldsTemp, 2*sizeof(FIELD4INFO)); /* L4TYPE & L4ID */
      i += 2;
      if (settings->time)
      {
         memcpy(changeFields+i, outFieldsTemp+L4TIME_F, sizeof(FIELD4INFO));
         i++;
      }
      if (settings->netid > 0)
      {
         memcpy(changeFields+i, outFieldsTemp+L4NETID_F, sizeof(FIELD4INFO));
         changeFields[i].len = settings->netid;
         i++;
      }
      if (settings->userid > 0)
      {
         memcpy(changeFields+i, outFieldsTemp+L4USERID_F, sizeof(FIELD4INFO));
         changeFields[i].len = settings->userid;
         i++;
      }
      if (settings->include & INCLUDE_ROLLBACK)
      {
         memcpy(changeFields+i, outFieldsTemp+L4ROLLED_F, sizeof(FIELD4INFO));
         i++;
      }
      if (settings->misc > 0)
      {
         memcpy(changeFields+i, outFieldsTemp+L4MISC_F, sizeof(FIELD4INFO));
         changeFields[i].len = settings->misc;
         i++;
      }
      memcpy(changeFields+i, outFieldsTemp+L4RECNO_F, 2*sizeof(FIELD4INFO)); /* L4RECNO & L4DELETED */
      i += 2;
      if (!origDbf)
      {
         memcpy(changeFields+i, outFieldsTemp+L4RECORD_F, sizeof(FIELD4INFO));
         if (logRecordWidth > 32767)
            changeFields[i].len = 32767;
         else
            changeFields[i].len = (unsigned)logRecordWidth;
         i++;
      }
      else
      {
         origFields = d4fieldInfo(origDbf);
         if (!origFields)
         {
            u4free(changeFields);
            chdir(curDir);
            u4free(curDir);
            #ifndef S4UNIX
               #ifndef S4OS2
               if (origDrive)
                  _dos_setdrive(origDrive, &dummy);
               #endif
            #endif
            u4free(outName);
            d4close(origDbf);
            return(-1);
         }
         for (j=0; j<d4numFields(origDbf) && i < tempNumFields; j++)
         {
            if (!(settings->include & INCLUDE_MEMO) && origFields[j].type == r4memo)
               continue;
            memcpy(changeFields+i, origFields+j, sizeof(FIELD4INFO));
            i++;
         }
         u4free(origFields);
      }
      memcpy(changeFields+i, outFieldsTemp+L4_F, sizeof(FIELD4INFO));

      c4->errCreate = 0;

      if( origDbf )
         if( origDbf->dataFile->version == 0x30 )
            c4->compatibility = 30 ;

      tempDbf = d4create(c4, outName, changeFields, 0);
      while (tempDbf == 0 && c4->errorCode == r4noCreate)
      {
         rc = getNextFileName(noPath(outName));
         if (rc != 0)
         {
            u4free(changeFields);
            chdir(curDir);
            u4free(curDir);
            #ifndef S4UNIX
               #ifndef S4OS2
               if (origDrive)
                  _dos_setdrive(origDrive, &dummy);
               #endif
            #endif
            u4free(outName);
            if (origDbf)
               d4close(origDbf);
            outMess[0] = "\r\n\r\nFile name error! Can not create unique name for output file for:\r\n";
            outMess[1] = addName;
            if( didProcess )
            {
               didProcess = 0;
               outMess[2] = "\r\n\r\nNOTE:\nSome analysis output files were successfully created before the"
                           "\r\nerror occurred. Check the date/time stamps on the files located in"
                           "\r\nthe specified output directory."
                           "\r\n\r\n";
            }
            else
               outMess[2] = "\r\n\r\n";
            util4out(c4, stdout, outMess, 3);
            return(rc);
         }
         tempDbf = d4create(c4, outName, changeFields, 0);
      }
      c4->errCreate = 1;
      u4free(changeFields);
      if (tempDbf == 0)
      {
         outMess[0] = "\r\n\r\nFile creation error! Can not create change file:\r\n";
         outMess[1] = outName;
         if( didProcess )
         {
            didProcess = 0;
            outMess[2] = "\r\n\r\nNOTE:\nSome analysis output files were successfully created before the"
                        "\r\nerror occurred. Check the date/time stamps on the files located in"
                        "\r\nthe specified output directory."
                        "\r\n\r\n";
         }
         else
            outMess[2] = "\r\n\r\n";

         util4out(c4, stdout, outMess, 3);

         chdir(curDir);
         u4free(curDir);
         #ifndef S4UNIX
            #ifndef S4OS2
            if (origDrive)
               _dos_setdrive(origDrive, &dummy);
            #endif
         #endif
         u4free(outName);
         if (origDbf)
            d4close(origDbf);
         return(-1);
      }
   }

   rc = chdir(curDir);
   u4free(curDir);
   #ifndef S4UNIX
      #ifndef S4OS2
      if (origDrive)
      {
         _dos_setdrive(origDrive, &dummy);
         _dos_getdrive(&curDrive);
         if (curDrive != origDrive)
            rc = -1;
      }
      #endif
   #endif
   u4free(outName);
   if (origDbf)
      rc = rc || d4close(origDbf);
   if (rc != 0)
      return(rc);

   util4rightJ(ltoa(tran4clientId(t4), clientId, 10), 12);
   d4tagSelect(openDbfs, d4tag(openDbfs, "CLID"));
   rc = d4seek(openDbfs, clientId);
   if (rc != 0)
   {
      outMess[0] = "\r\n\r\nLog file error! User not initialized.\r\n\r\n";
      util4out(c4, stdout, outMess, 1);
      d4close(tempDbf);
      return(rc);
   }

   rc = d4appendStart(openDbfs, 0);
   if (rc != 0)
      return(rc);
   f4assignLong(d4field(openDbfs, "SERVERDTID"), tran4serverDataId(t4));
   f4assignLong(d4field(openDbfs, "RECORDWID"), logRecordWidth);
   f4assignLong(d4field(openDbfs, "DATA4PTR"), (long)tempDbf);
   f4assign(d4field(openDbfs, "FILENAME"), addName);
   if (tran4type(t4) == TRAN4OPEN_TEMP)
      f4assignChar(d4field(openDbfs, "TEMP"), 'Y');
   rc = d4append(openDbfs);
   if (rc == r4unique)
   {
      outMess[0] = "\r\n\r\nLog file error! DBF identifier already used.\r\n\r\n";
      util4out(c4, stdout, outMess, 1);
   }
   if (rc != 0)
      return(rc);

   if (settings->include & INCLUDE_OPEN)
   {
      rc = d4appendStart(tempDbf, 0);
      if (rc != 0)
         return(rc);
      d4blank(tempDbf);
      if (tran4type(t4) == TRAN4OPEN)
         f4assign(d4field(tempDbf, "L4TYPE"), "OPEN");
      else
         f4assign(d4field(tempDbf, "L4TYPE"), "OPEN_TEMP");
      f4assignLong(d4field(tempDbf, "L4ID"), tran4id(t4));
      if (settings->time)
         f4assign(d4field(tempDbf, "L4TIME"), timeformat(t4));
      if (settings->netid > 0)
         f4assignField(d4field(tempDbf, "L4NETID"), d4field(openDbfs, "NETNAME"));
      if (settings->userid > 0)
         f4assignField(d4field(tempDbf, "L4USERID"), d4field(openDbfs, "USERNAME"));
      if (settings->misc > 0)
         f4assign(d4field(tempDbf, "L4MISC"), addName);
      rc = d4append(tempDbf);
   }

   if ( !rc && !didProcess )
      didProcess = 1;

   return(rc);
}

/****************************************************************************
**::  Function: typeTran4Close
**
*****************************************************************************
**
**    Function: typeTran4Close()
**
****************************************************************************/
static short typeTran4Close(CODE4 *c4, SETTINGSTRUCT *settings, DATA4 *openDbfs, TRAN4 *t4)
{
   DATA4 *tempDbf;
   short rc;
   char client[23];

   util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
   util4rightJ(ltoa(tran4serverDataId(t4), client+11, 10), 12);
   d4tagSelect(openDbfs, d4tag(openDbfs, "CLID"));
   rc = d4seek(openDbfs, client);
   if (rc != 0)
   {
      if (rc == r4after || rc == r4eof)
         return(0);
      return(rc);
   }
   d4delete(openDbfs);

   if (settings->include & INCLUDE_OPEN)
   {
      tempDbf = (DATA4 *)f4long(d4field(openDbfs, "DATA4PTR"));
      rc = d4appendStart(tempDbf, 0);
      if (rc != 0)
         return(rc);
      d4blank(tempDbf);
      if (f4true(d4field(openDbfs, "TEMP")))
         f4assign(d4field(tempDbf, "L4TYPE"), "CLOSE_TEMP");
      else
         f4assign(d4field(tempDbf, "L4TYPE"), "CLOSE");
      f4assignLong(d4field(tempDbf, "L4ID"), tran4id(t4));
      if (settings->time)
         f4assign(d4field(tempDbf, "L4TIME"), timeformat(t4));
      if (settings->netid > 0)
         f4assignField(d4field(tempDbf, "L4NETID"), d4field(openDbfs, "NETNAME"));
      if (settings->userid > 0)
         f4assignField(d4field(tempDbf, "L4USERID"), d4field(openDbfs, "USERNAME"));
      if (settings->misc > 0)
         f4assignField(d4field(tempDbf, "L4MISC"), d4field(openDbfs, "FILENAME"));
      rc = d4append(tempDbf);
   }
   return(rc);
}

/****************************************************************************
**::  Function: typeTran4Start
**
*****************************************************************************
**
**    Function: typeTran4Start()
**
****************************************************************************/
short typeTran4Start(CODE4 *c4, SETTINGSTRUCT *settings, DATA4 *openDbfs, DATA4* tranDbf, TRAN4 *t4)
{
   DATA4 *tempDbf;
   short rc, rolledBack;
   char clientId[12], *outMess[1];

   util4rightJ(ltoa(tran4clientId(t4), clientId, 10), 12);
   d4tagSelect(openDbfs, d4tag(openDbfs, "CLID"));
   rc = d4seek(openDbfs, clientId);
   if (rc != 0)
   {
      outMess[0] = "\r\n\r\nLog file error! User not initialized.\r\n\r\n";
      util4out(c4, stdout, outMess, 1);
      return(rc);
   }
   rc = d4skip(openDbfs, 1L);
   if (rc != 0 && rc != r4eof)
      return(rc);
   while (f4long(d4field(openDbfs, "SERVERDTID")) != 0 && rc == 0)
   {
      f4assignLong(d4field(openDbfs, "TRANID"), tran4id(t4));
      if (settings->include & INCLUDE_ROLLBACK)
      {
         tempDbf = (DATA4 *)f4long(d4field(openDbfs, "DATA4PTR"));
         rc = d4appendStart(tempDbf, 0);
         if (rc != 0)
            return(rc);
         d4blank(tempDbf);
         f4assign(d4field(tempDbf, "L4TYPE"), "START");
         f4assignLong(d4field(tempDbf, "L4ID"), tran4id(t4));
         if (settings->time)
            f4assign(d4field(tempDbf, "L4TIME"), timeformat(t4));
         if (settings->netid > 0)
            f4assignField(d4field(tempDbf, "L4NETID"), d4field(openDbfs, "NETNAME"));
         if (settings->userid > 0)
            f4assignField(d4field(tempDbf, "L4USERID"), d4field(openDbfs, "USERNAME"));
         rolledBack = 1;
         util4rightJ(ltoa(tran4id(t4), clientId, 10), 12);
         rc = d4seek(tranDbf, clientId);
         if (rc != 0)
            if (rc == r4after || rc == r4eof)
               rolledBack = 0;
            else
               return(rc);
         if (rolledBack)
            f4assignChar(d4field(tempDbf, "L4ROLLED"), 'Y');
         rc = d4append(tempDbf);
         if (rc != 0)
            return(rc);
      }
      rc = d4skip(openDbfs, 1L);
   }
   if (rc == r4eof)
      rc = 0;
   return(rc);
}

/****************************************************************************
**::  Function: typeTran4CompleteRollback
**
*****************************************************************************
**
**    Function: typeTran4CompleteRollback()
**
****************************************************************************/
short typeTran4CompleteRollback(CODE4 *c4, SETTINGSTRUCT *settings, DATA4 *openDbfs, TRAN4 *t4)
{
   DATA4 *tempDbf;
   short rc;
   char tranId[12];

   util4rightJ(ltoa(tran4id(t4), tranId, 10), 12);
   d4tagSelect(openDbfs, d4tag(openDbfs, "TRANID"));
   rc = d4seek(openDbfs, tranId);
   if (rc != 0 && rc != r4eof && rc != r4after)
      return(rc);
   while (rc == 0)
   {
      f4assignLong(d4field(openDbfs, "TRANID"), 0);
      if (settings->include & INCLUDE_ROLLBACK)
      {
         tempDbf = (DATA4 *)f4long(d4field(openDbfs, "DATA4PTR"));
         rc = d4appendStart(tempDbf, 0);
         if (rc != 0)
            return(rc);
         d4blank(tempDbf);
         if (tran4type(t4) == TRAN4COMPLETE)
            f4assign(d4field(tempDbf, "L4TYPE"), "COMPLETE");
         else
            f4assign(d4field(tempDbf, "L4TYPE"), "ROLLBACK");
         f4assignLong(d4field(tempDbf, "L4ID"), tran4id(t4));
         if (settings->time)
            f4assign(d4field(tempDbf, "L4TIME"), timeformat(t4));
         if (settings->netid > 0)
            f4assignField(d4field(tempDbf, "L4NETID"), d4field(openDbfs, "NETNAME"));
         if (settings->userid > 0)
            f4assignField(d4field(tempDbf, "L4USERID"), d4field(openDbfs, "USERNAME"));
         rc = d4append(tempDbf);
         if (rc != 0)
            return(rc);
      }
      rc = d4seek(openDbfs, tranId);
   }
   if (rc == r4eof || rc == r4after)
      rc = 0;
   return(rc);
}

/****************************************************************************
**::  Function: typeTran4Write
**
*****************************************************************************
**
**    Function: typeTran4Write()
**
****************************************************************************/
static short typeTran4Write(CODE4 *c4, SETTINGSTRUCT *settings, DATA4 *openDbfs, DATA4* tranDbf, TRAN4 *t4)
{
   DATA4 *tempDbf;
   short rc, i, rolledBack=1;
   unsigned long offset;
   char client[23];
   char *data;
   unsigned long offsetMemo;

   util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
   util4rightJ(ltoa(tran4serverDataId(t4), client+11, 10), 12);
   d4tagSelect(openDbfs, d4tag(openDbfs, "CLID"));
   rc = d4seek(openDbfs, client);
   if (rc != 0)
   {
      if (rc == r4after || rc == r4eof)
         return(0);
      return(rc);
   }
   util4rightJ(ltoa(tran4id(t4), client, 10), 12);
   rc = d4seek(tranDbf, client);
   if (rc != 0)
      if (rc == r4after || rc == r4eof)
         rolledBack = 0;
      else
         return(rc);
   if (!(settings->include & INCLUDE_ROLLBACK) && rolledBack)
      return(0);

   tempDbf = (DATA4 *)f4long(d4field(openDbfs, "DATA4PTR"));
   data = (char *)tran4getData(t4, 0L);
   if (!data)
      return(-1);

   if (settings->include & INCLUDE_INITIAL)
   {
      rc = d4appendStart(tempDbf, 0);
      if (rc != 0)
         return(rc);
      d4blank(tempDbf);
      f4assign(d4field(tempDbf, "L4TYPE"), "WRITE_OLD");
      f4assignLong(d4field(tempDbf, "L4ID"), tran4id(t4));
      if (settings->time)
         f4assign(d4field(tempDbf, "L4TIME"), timeformat(t4));
      if (settings->netid > 0)
         f4assignField(d4field(tempDbf, "L4NETID"), d4field(openDbfs, "NETNAME"));
      if (settings->userid > 0)
         f4assignField(d4field(tempDbf, "L4USERID"), d4field(openDbfs, "USERNAME"));
      if (rolledBack)
         f4assignChar(d4field(tempDbf, "L4ROLLED"), 'Y');
      f4assignLong(d4field(tempDbf, "L4RECNO"), *(long *)data);
      if (*(data+4) == '*')
         f4assignChar(d4field(tempDbf, "L4DELETED"), 'Y');
      offset = 5;
      offsetMemo = 4+f4long(d4field(openDbfs, "RECORDWID"))*2;
      if (!d4field(tempDbf, "L4RECORD"))
         for (i=d4fieldNumber(tempDbf, "L4DELETED")+1; i<=d4numFields(tempDbf); i++)
            if (f4type(d4fieldJ(tempDbf, i)) == 'M')
            {
               if (*(unsigned long *)(data+offsetMemo) != 0)
                  f4memoAssignN(d4fieldJ(tempDbf, i), data+offsetMemo+4, *(unsigned *)(data+offsetMemo));
               offsetMemo += 4 + *(unsigned long *)(data+offsetMemo);
               offsetMemo += 4 + *(unsigned long *)(data+offsetMemo);
               offset += 10;
            }
            else
            {
               f4assignN(d4fieldJ(tempDbf, i), data+offset, f4len(d4fieldJ(tempDbf, i)));
               offset += f4len(d4fieldJ(tempDbf, i));
            }
      else
         f4assignN(d4field(tempDbf, "L4RECORD"), data+offset, (unsigned)f4long(d4field(openDbfs, "RECORDWID"))-1);
      rc = d4append(tempDbf);
      if (rc != 0)
         return(rc);
   }

   if (!(settings->include & INCLUDE_UPDATE))
      return(0);
   rc = d4appendStart(tempDbf, 0);
   if (rc != 0)
      return(rc);
   d4blank(tempDbf);
   f4assign(d4field(tempDbf, "L4TYPE"), "WRITE_NEW");
   f4assignLong(d4field(tempDbf, "L4ID"), tran4id(t4));
   if (settings->time)
      f4assign(d4field(tempDbf, "L4TIME"), timeformat(t4));
   if (settings->netid > 0)
      f4assignField(d4field(tempDbf, "L4NETID"), d4field(openDbfs, "NETNAME"));
   if (settings->userid > 0)
      f4assignField(d4field(tempDbf, "L4USERID"), d4field(openDbfs, "USERNAME"));
   if (rolledBack)
      f4assignChar(d4field(tempDbf, "L4ROLLED"), 'Y');
   f4assignLong(d4field(tempDbf, "L4RECNO"), *(long *)data);
   offset = 4+f4long(d4field(openDbfs, "RECORDWID"));
   offsetMemo = 4+f4long(d4field(openDbfs, "RECORDWID"))*2;
   if (*(data+offset++) == '*')
      f4assignChar(d4field(tempDbf, "L4DELETED"), 'Y');
   if (!d4field(tempDbf, "L4RECORD"))
      for (i=d4fieldNumber(tempDbf, "L4DELETED")+1; i<=d4numFields(tempDbf); i++)
         if (f4type(d4fieldJ(tempDbf, i)) == 'M')
         {
            offsetMemo += 4 + (unsigned long) *(data+offsetMemo);
            if (*(unsigned long *)(data+offsetMemo) != 0)
               f4memoAssignN(d4fieldJ(tempDbf, i), data+offsetMemo+4, *(unsigned *)(data+offsetMemo));
            offsetMemo += 4 + *(unsigned long *)(data+offsetMemo);
            offset += 10;
         }
         else
         {
            f4assignN(d4fieldJ(tempDbf, i), data+offset, f4len(d4fieldJ(tempDbf, i)));
            offset += f4len(d4fieldJ(tempDbf, i));
         }
   else
      f4assignN(d4field(tempDbf, "L4RECORD"), data+offset, (unsigned)f4long(d4field(openDbfs, "RECORDWID"))-1);
   rc = d4append(tempDbf);
   return(rc);
}

/****************************************************************************
**::  Function: typeTran4Append
**
*****************************************************************************
**
**    Function: typeTran4Append()
**
****************************************************************************/
static short typeTran4Append(CODE4 *c4, SETTINGSTRUCT *settings, DATA4 *openDbfs, DATA4* tranDbf, TRAN4 *t4)
{
   DATA4 *tempDbf;
   short rc, i, rolledBack=1;
   unsigned short offset;
   char client[23];
   char *data;
   unsigned long offsetMemo;

   if (!(settings->include & INCLUDE_UPDATE))
      return(0);
   util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
   util4rightJ(ltoa(tran4serverDataId(t4), client+11, 10), 12);
   d4tagSelect(openDbfs, d4tag(openDbfs, "CLID"));
   rc = d4seek(openDbfs, client);
   if (rc != 0)
   {
      if (rc == r4after || rc == r4eof)
         return(0);
      return(rc);
   }
   util4rightJ(ltoa(tran4id(t4), client, 10), 12);
   rc = d4seek(tranDbf, client);
   if (rc != 0)
      if (rc == r4after || rc == r4eof)
         rolledBack = 0;
      else
         return(rc);
   if (!(settings->include & INCLUDE_ROLLBACK) && rolledBack)
      return(0);

   tempDbf = (DATA4 *)f4long(d4field(openDbfs, "DATA4PTR"));
   data = (char *)tran4getData(t4, 0L);
   if (!data)
      return(-1);

   rc = d4appendStart(tempDbf, 0);
   if (rc != 0)
      return(rc);
   d4blank(tempDbf);
   f4assign(d4field(tempDbf, "L4TYPE"), "APPEND");
   f4assignLong(d4field(tempDbf, "L4ID"), tran4id(t4));
   if (settings->time)
      f4assign(d4field(tempDbf, "L4TIME"), timeformat(t4));
   if (settings->netid > 0)
      f4assignField(d4field(tempDbf, "L4NETID"), d4field(openDbfs, "NETNAME"));
   if (settings->userid > 0)
      f4assignField(d4field(tempDbf, "L4USERID"), d4field(openDbfs, "USERNAME"));
   if (rolledBack)
      f4assignChar(d4field(tempDbf, "L4ROLLED"), 'Y');
   f4assignLong(d4field(tempDbf, "L4RECNO"), *(long *)data);
   if (*(data+4) == '*')
      f4assignChar(d4field(tempDbf, "L4DELETED"), 'Y');
   offset = 5;
   offsetMemo = 4+f4long(d4field(openDbfs, "RECORDWID"));
   if (!d4field(tempDbf, "L4RECORD"))
      for (i=d4fieldNumber(tempDbf, "L4DELETED")+1; i<=d4numFields(tempDbf); i++)
         if (f4type(d4fieldJ(tempDbf, i)) == 'M')
         {
            if (*(unsigned long *)(data+offsetMemo) != 0)
               f4memoAssignN(d4fieldJ(tempDbf, i), data+offsetMemo+4, *(unsigned *)(data+offsetMemo));
            offsetMemo += 4 + *(unsigned long *)(data+offsetMemo);
            offset += 10;
         }
         else
         {
            f4assignN(d4fieldJ(tempDbf, i), data+offset, f4len(d4fieldJ(tempDbf, i)));
            offset += f4len(d4fieldJ(tempDbf, i));
         }
   else
      f4assignN(d4field(tempDbf, "L4RECORD"), data+offset, (unsigned)f4long(d4field(openDbfs, "RECORDWID"))-1);
   rc = d4append(tempDbf);
   return(rc);
}

/****************************************************************************
**::  Function: typeTran4PackZap
**
*****************************************************************************
**
**    Function: typeTran4PackZap()
**
****************************************************************************/
static short typeTran4PackZap(CODE4 *c4, SETTINGSTRUCT *settings, DATA4 *openDbfs, TRAN4 *t4)
{
   DATA4 *tempDbf;
   short rc;
   char client[23];
   char *data, buff[49];

   if (!(settings->include & INCLUDE_UPDATE))
      return(0);
   util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
   util4rightJ(ltoa(tran4serverDataId(t4), client+11, 10), 12);
   d4tagSelect(openDbfs, d4tag(openDbfs, "CLID"));
   rc = d4seek(openDbfs, client);
   if (rc != 0)
   {
      if (rc == r4after || rc == r4eof)
         return(0);
      return(rc);
   }

   tempDbf = (DATA4 *)f4long(d4field(openDbfs, "DATA4PTR"));
   data = (char *)tran4getData(t4, 0L);
   if (!data)
      return(-1);

   rc = d4appendStart(tempDbf, 0);
   if (rc != 0)
      return(rc);
   d4blank(tempDbf);
   if (tran4type(t4) == TRAN4ZAP)
      f4assign(d4field(tempDbf, "L4TYPE"), "ZAP");
   else
      f4assign(d4field(tempDbf, "L4TYPE"), "PACK");
   f4assignLong(d4field(tempDbf, "L4ID"), tran4id(t4));
   if (settings->time)
      f4assign(d4field(tempDbf, "L4TIME"), timeformat(t4));
   if (settings->netid > 0)
      f4assignField(d4field(tempDbf, "L4NETID"), d4field(openDbfs, "NETNAME"));
   if (settings->userid > 0)
      f4assignField(d4field(tempDbf, "L4USERID"), d4field(openDbfs, "USERNAME"));
   if (tran4type(t4) == TRAN4ZAP && settings->misc > 0)
   {
      sprintf(buff, "Begin Recno: %lu, End Recno: %lu", *(unsigned long *)data, *(unsigned long *)(data+4));
      f4assign(d4field(tempDbf, "L4MISC"), buff);
   }
   rc = d4append(tempDbf);
   return(rc);
}

/****************************************************************************
**::  Function: doLogFile
**
*****************************************************************************
**
**    Function: doLogFile()
**
****************************************************************************/
static int doLogFile(CODE4 *c4, SETTINGSTRUCT *settings)
{
   DATA4 *openDbfs, *tranDbf, *tempDbf;
   TRAN4 *t4;
   FIELD4 *data4Ptr;
   short rc, rc2, intPart, decPart;
   char *outMess[3];
   char client[23];
   long logFileLen;
   TRAN4ENTRY_LEN len;

   rc = file4refresh(&c4->transFile.file);
   if (rc != 0)
      return(rc);
   logFileLen = file4len(&c4->transFile.file);
   if (logFileLen < 0)
      return((short)logFileLen);

   openDbfs = d4createTemp(c4, openFields, openTags);
   if (openDbfs == 0)
      return(-1);
   tranDbf = d4createTemp(c4, tranFields, tranTags);
   if (tranDbf == 0)
   {
      d4close(openDbfs);
      return(-1);
   }
   t4 = &c4->c4trans.trans;
   c4->autoOpen = 0;
   d4tagSelect(tranDbf, d4tag(tranDbf, "TRANID_T"));
   #ifndef S4WINDOWS
      outMess[0] = statusStringBlank;
      util4out(c4, stderr, outMess, 1);
   #endif

   for (rc = tran4top(t4); rc == 0; rc = tran4skip(t4, TRAN4FORWARDS))
   {
      intPart = (short)((unsigned long)c4->c4trans.trans.pos*50/logFileLen);
      decPart = (short)((unsigned long)c4->c4trans.trans.pos*5000/logFileLen%100);
      #ifndef S4WINDOWS
         sprintf(statusStringSpace, statusStringFormt, intPart, decPart);
         outMess[0] = statusStringBckSp;
         outMess[1] = statusStringMessg;
         outMess[2] = statusStringSpace;
         util4out(c4, stderr, outMess, 3);
      #else
         sendStatusMessage( hWndStatus, intPart ) ;
      #endif

      switch (tran4type(t4))
      {
         case TRAN4SHUTDOWN:
         case TRAN4BACKEDUP:
         case TRAN4INIT:
         case TRAN4INIT_UNDO:
         case TRAN4OPEN:
         case TRAN4OPEN_TEMP:
         case TRAN4CLOSE:
         case TRAN4WRITE:
         case TRAN4APPEND:
         case TRAN4PACK:
         case TRAN4ZAP:
         case TRAN4VOID:
         case TRAN4ROLLBACK:
            break;

         case TRAN4START:
            rc = d4appendStart(tranDbf, 0);
            if (rc != 0)
               break;
            d4blank(tranDbf);
            f4assignLong(d4field(tranDbf, "TRANID"), tran4id(t4));
            rc = d4append(tranDbf);
            if (rc == r4unique)
            {
               outMess[0] = "\r\n\r\nLog file error! Transaction ID already started.\r\n\r\n";
               util4out(c4, stdout, outMess, 1);
            }
            break;

         case TRAN4COMPLETE:
            util4rightJ(ltoa(tran4id(t4), client, 10), 12);
            rc = d4seek(tranDbf, client);
            if (rc != 0)
            {
               if (rc == r4after || rc == r4eof)
               {
                  outMess[0] = "\r\n\r\nLog file error! Transaction completed that was not started.\r\n\r\n";
                  util4out(c4, stdout, outMess, 1);
                  rc = -1;
               }
               break;
            }
            d4delete(tranDbf);
            break;

         default:
            outMess[0] = "\r\n\r\nLog file error! Unknown log entry type.\r\n\r\n";
            util4out(c4, stdout, outMess, 1);
            rc = -1;
      }
      if (rc != 0)
         break;
      if (t4->pos+sizeof(LOG4HEADER)+sizeof(TRAN4ENTRY_LEN) > logFileLen)
      {
         rc = r4eof;
         break;
      }
      rc = file4readAll(&c4->transFile.file, t4->pos+sizeof(LOG4HEADER), &len, sizeof(TRAN4ENTRY_LEN));
      if (rc == 0 && (long)(t4->pos+sizeof(LOG4HEADER)+len) > logFileLen)
      {
         rc = r4eof;
         break;
      }
      if (rc != 0)
         break;
   }

   if (rc == r4eof)
   {
      for (rc = tran4top(t4); rc == 0; rc = tran4skip(t4, TRAN4FORWARDS))
      {
         intPart = (short)((unsigned long)c4->c4trans.trans.pos*50/logFileLen+50);
         decPart = (short)((unsigned long)c4->c4trans.trans.pos*5000/logFileLen%100);
         #ifndef S4WINDOWS
            sprintf(statusStringSpace, statusStringFormt, intPart, decPart);
            outMess[0] = statusStringBckSp;
            outMess[1] = statusStringMessg;
            outMess[2] = statusStringSpace;
            util4out(c4, stderr, outMess, 3);
         #else
            sendStatusMessage( hWndStatus, intPart ) ;
         #endif

         switch (tran4type(t4))
         {
            case TRAN4SHUTDOWN:
               rc = typeTran4Shutdown(c4, openDbfs, t4);
               break;

            case TRAN4INIT:
               rc = typeTran4Init(c4, openDbfs, t4);
               break;

            case TRAN4INIT_UNDO:
               rc = typeTran4InitUndo(c4, openDbfs, t4);
               break;

            case TRAN4OPEN:
            case TRAN4OPEN_TEMP:
               rc = typeTran4Open(c4, settings, openDbfs, t4);
               break;

            case TRAN4CLOSE:
               rc = typeTran4Close(c4, settings, openDbfs, t4);
               break;

            case TRAN4START:
               rc = typeTran4Start(c4, settings, openDbfs, tranDbf, t4);
               break;

            case TRAN4COMPLETE:
            case TRAN4ROLLBACK:
               rc = typeTran4CompleteRollback(c4, settings, openDbfs, t4);
               break;

            case TRAN4WRITE:
               rc = typeTran4Write(c4, settings, openDbfs, tranDbf, t4);
               break;

            case TRAN4APPEND:
               rc = typeTran4Append(c4, settings, openDbfs, tranDbf, t4);
               break;

            case TRAN4PACK:
            case TRAN4ZAP:
               rc = typeTran4PackZap(c4, settings, openDbfs, t4);
               break;

            case TRAN4BACKEDUP:
            case TRAN4VOID:
               break;

            default:
               outMess[0] = "\r\n\r\nLog file error! Unknown log entry type.\r\n\r\n";
               util4out(c4, stdout, outMess, 1);
               rc = -1;
         }
         if (rc != 0)
         {
            if (rc == r4bof || rc == r4eof)
               rc = -1;
            break;
         }
         if (t4->pos+sizeof(LOG4HEADER)+sizeof(TRAN4ENTRY_LEN) > logFileLen)
         {
            rc = r4eof;
            break;
         }
         rc = file4readAll(&c4->transFile.file, t4->pos+sizeof(LOG4HEADER), &len, sizeof(TRAN4ENTRY_LEN));
         if (rc == 0 && (long)(t4->pos+sizeof(LOG4HEADER)+len) > logFileLen)
            rc = r4eof;
         if (rc != 0)
            break;
      }
      if (rc == r4eof)
         rc = 0;
   }
   if (rc == 0)
   {
      #ifndef S4WINDOWS
         outMess[0] = statusStringBckSp;
         outMess[1] = statusStringMessg;
         sprintf(statusStringSpace, statusStringFormt, 100, 0);
         outMess[2] = statusStringSpace;
         util4out(c4, stderr, outMess, 3);
      #endif
   }

   #ifndef S4WINDOWS
      outMess[0] = "\r\n";
      util4out(c4, stderr, outMess, 1);
      util4out(c4, stdout, outMess, 1);
   #endif
   rc2 = rc;
   d4tagSelect(openDbfs, 0);
   data4Ptr = d4field(openDbfs, "DATA4PTR");
   for (rc = d4top(openDbfs); rc == 0; rc = d4skip(openDbfs, 1L))
   {
      tempDbf = (DATA4 *)f4long(data4Ptr);
      if (tempDbf)
         rc2 = rc2 || d4close(tempDbf);
   }
   if (rc == r4eof)
      rc = rc2;
   rc = rc || d4close(tranDbf);
   rc = rc || d4close(openDbfs);
   return(rc);
}

/****************************************************************************
**::  Function: main
**
*****************************************************************************
**
**    Function: main()
**
****************************************************************************/
#ifdef S4DLL_BUILD
int S4FUNCTION  mainLog4Dbf(int argc, char *argv[], int hWnd )
#else
int main(int argc, char *argv[])
#endif
{
   CODE4 c4;
   TRAN4 *t4;
   SETTINGSTRUCT settings;
   int rc=0;
   char *logFileName=0, *outMess[1];

   #ifdef S4WINDOWS
      hWndStatus = hWnd ;
      messageType = MB_ICONSTOP ;
   #endif

   #ifndef S4DLL_BUILD
   #ifdef _WINDOWS
      _wabout("LOG4DBF.EXE\r\n(c)Copyright Sequiter Software Inc., 1988-1996");
   #endif
   #endif

   #ifdef S4UNIX
      strcpy(statusStringSpace, "100.00  ");
   #endif

   code4init(&c4);
   c4.autoOpen = 1;
   c4.errFieldName = 0;
   c4.lockAttempts = WAIT4EVER;
   c4.safety = 1;
   c4.singleOpen = 0 ;
   memset(&settings, 0, sizeof(SETTINGSTRUCT));

   #ifndef S4WINDOWS
      outMess[0] = "\r\n";
      util4out(&c4, stderr, outMess, 1);
   #endif
   if (argc < 2)
      rc = -1;
   if (rc == 0)
      rc = util4parseCommandLine(&c4, u4flags, u4flagsNum, argv, argc);
   if (rc == 0)
      rc = checkFlags(&c4, u4flags, u4flagsNum, &settings);
   if (rc != 0)
   {
      util4printCommandLine(&c4, u4flags, u4flagsNum, argv[0]);
      code4initUndo(&c4);
      #ifdef S4WINDOWS
         sendCloseMessage( hWndStatus ) ;
      #endif
      return(rc);
   }

   rc = getLogFileName(&c4, &settings, &logFileName);
   if (rc == 0)
   {
      rc = code4logOpen( &c4, logFileName, "" ) ;
      code4tranStatusSet(&c4, r4off);
   }
   if (rc == 0 && sizeof(LOG4HEADER)+sizeof(TRAN4ENTRY_LEN) > file4len(&c4.transFile.file))
   {
      rc = file4lenSet(&c4.transFile.file, 0L);
      outMess[0] = "Log file error! Log file is empty.\r\n\r\n";
      util4out(&c4, stdout, outMess, 1);
      rc = -1;
   }
   if (rc == 0)
   {
      t4 = &c4.c4trans.trans;
      rc = tran4top(t4);
      if (rc == 0 && ((tran4type(t4) != TRAN4SHUTDOWN && tran4type(t4) != TRAN4BACKEDUP) || tran4serverDataId(t4) > TRAN4VERSION_NUM))
      {
         outMess[0] = "Log file error! File is either not valid or a newer version than this utility can handle!\r\n\r\n";
         util4out(&c4, stdout, outMess, 1);
         rc = -1;
      }
   }

   if (rc == 0)
      rc = doLogFile(&c4, &settings);
   if (rc == 0)
   {
      #ifdef S4WINDOWS
         messageType = MB_ICONEXCLAMATION ;
         sendStatusMessage( hWndStatus, 100 ) ;
      #endif
      outMess[0] = "Utility successfully finished!\r\n";
   }
   else
      outMess[0] = "Error! Utility did not successfully finish!\r\n";
   util4out(&c4, stdout, outMess, 1);

   code4transInitUndo(&c4.c4trans);
   if (logFileName)
      u4free(logFileName);
   util4flagsFree(u4flags, u4flagsNum);
   code4initUndo(&c4);
   #ifdef S4WINDOWS
      sendCloseMessage( hWndStatus ) ;
   #endif
   return(rc);
}