/* log4fix.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */
/* Fixes corrupt server .LOG files */

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

#include "d4all.h"

#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

#ifndef S4UTIL_CONCURRENCY
   #define UTIL4LOG    0
#else
   #define UTIL4CONFIG 0
#endif
#define UTIL4FORCE     1

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

static UTIL4FLAG u4flags[] =
{
#ifndef S4UTIL_CONCURRENCY
   { "Log",       0, 1, 1,  1, 1, 0, 0, 0, "{-log <server.log>}\r\n"      },
#else
   { "Config",    0, 1, 1, -1, 0, 0, 0, 0, "{-config [<config.dbf>]}\r\n" },
#endif
   { "Force",     0, 1, 1,  1, 1, 0, 0, 0, "[-force {OFF | ON}]\r\n"      },
};

static int u4flagsNum = 2;

static FIELD4INFO openFields[] =
{
   { "CLIENTID",   'N', 11,       0 },
   { "CLIENTDTID", 'N', 11,       0 },
   { "SERVERDTID", 'N', 11,       0 },
   { "PHYSNAME",   'C', LEN4PATH, 0 },
   { "RECORDWID",  'N', 11,       0 },
   { "NUMFIELDS",  'N',  6,       0 },
   { 0,0,0,0 },
};

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

static FIELD4INFO tranFields[] =
{
   { "TRANID",   'N', 11, 0 },
   { 0,0,0,0 },
} ;

static TAG4INFO tranTags[] =
{
   { "TRANID", "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: findIncompleteTran
**
*****************************************************************************
**
**    Function: findIncompleteTran()
**
****************************************************************************/
short findIncompleteTran(CODE4 *c4, DATA4 *openDbf, DATA4 *tranDbf)
{
   short rc, intPart, decPart;
   TRAN4 *t4=0;
   FIELD4 *openClientId=0, *openClientDataId=0, *openServerDataId=0, *openPhysName=0, *tranTranId=0, *tranClientId=0;
   FIELD4 *openRecordWidth=0, *openNumFields=0;
   char client[23], *outMess[3];
   char *data=0;
   long logFileLen;
   TRAN4ENTRY_LEN len;
   #ifdef S4DATA_ALIGN
      long templong;
      short tempshort;
   #endif

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

   t4 = &c4->c4trans.trans;
   openClientId = d4field(openDbf, "CLIENTID");
   openClientDataId = d4field(openDbf, "CLIENTDTID");
   openServerDataId = d4field(openDbf, "SERVERDTID");
   openPhysName = d4field(openDbf, "PHYSNAME");
   openRecordWidth = d4field(openDbf, "RECORDWID");
   openNumFields = d4field(openDbf, "NUMFIELDS");
   d4tagSelect(openDbf, d4tag(openDbf, "CLID"));

   tranTranId = d4field(tranDbf, "TRANID");
   d4tagSelect(tranDbf, d4tag(tranDbf, "TRANID"));
   #ifndef S4WINDOWS
      outMess[0] = statusStringBlank;
      util4out(c4, stderr, outMess, 1);
   #endif

   for (rc = tran4top(t4); ; rc = tran4skip(t4, TRAN4FORWARDS))
   {
      if (rc != 0)
         break;
      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 TRAN4BACKEDUP:
         case TRAN4WRITE:
         case TRAN4APPEND:
         case TRAN4PACK:
         case TRAN4ZAP:
         case TRAN4VOID:
            break;

         case TRAN4SHUTDOWN:
            rc = d4pack(openDbf);
            if (rc != 0)
               break;
            if (d4recCount(openDbf) > 0)
            {
               outMess[0] = "\r\n\r\nLog file corrupt! User(s) initialized at shutdown, continuing.\r\n\r\n";
               #ifndef S4WINDOWS
                  outMess[1] = statusStringBlank;
                  util4out(c4, stdout, outMess, 2);
               #else
                  util4out(c4, stdout, outMess, 1);
               #endif
               rc = d4zap(openDbf, 1L, d4recCount(openDbf));
               if (rc != 0)
                  break;
            }
            rc = d4pack(tranDbf);
            if (rc != 0)
               break;
            if (d4recCount(tranDbf) > 0)
            {
               outMess[0] = "\r\n\r\nLog file corrupt! Transaction(s) active at shutdown, continuing.\r\n\r\n";
               #ifndef S4WINDOWS
                  outMess[1] = statusStringBlank;
                  util4out(c4, stdout, outMess, 2);
               #else
                  util4out(c4, stdout, outMess, 1) ;
               #endif

               rc = d4zap(tranDbf, 1L, d4recCount(tranDbf));
               if (rc != 0)
                  break;
            }
            if (tran4serverDataId(&c4->c4trans.trans) > 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);
               rc = -1;
            }
            break;

         case TRAN4INIT:
            rc = d4appendStart(openDbf, 0);
            if (rc != 0)
               break;
            d4blank(openDbf);
            f4assignLong(openClientId, tran4clientId(t4));
            rc = d4append(openDbf);
            if (rc == r4unique)
            {
               outMess[0] = "\r\n\r\nLog file error! User already initialized.\r\n\r\n";
               util4out(c4, stdout, outMess, 1);
            }
            break;

         case TRAN4INIT_UNDO:
            util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
            rc = d4seek(openDbf, client);
            if (rc == 0 && f4long(openClientDataId) != 0)
               rc = r4after;
            if (rc != 0)
            {
               if (rc == r4after || rc == r4eof)
               {
                  outMess[0] = "\r\n\r\nLog file error! User uninitialized that is not initialized.\r\n\r\n";
                  util4out(c4, stdout, outMess, 1);
                  rc = -1;
               }
               break;
            }
            d4delete(openDbf);
            rc = d4seek(openDbf, client);
            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);
               rc = -1;
               break;
            }
            if (rc == r4after || rc == r4eof)
               rc = 0;
            break;

         case TRAN4OPEN:
         case TRAN4OPEN_TEMP:
            data = (char *)tran4getData(t4, 0L);
            if (data == 0)
            {
               rc = -1;
               break;
            }
            util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
            rc = d4seek(openDbf, client);
            if (rc != 0)
            {
               if (rc == r4after || rc == r4eof)
               {
                  outMess[0] = "\r\n\r\nLog file error! DBF opened by user that is not initialized.\r\n\r\n";
                  util4out(c4, stdout, outMess, 1);
                  rc = -1;
               }
               break;
            }
            rc = d4appendStart(openDbf, 0);
            if (rc != 0)
               break;
            f4assignLong(openClientDataId, tran4clientDataId(t4));
            f4assignLong(openServerDataId, tran4serverDataId(t4));
            f4assignN(openPhysName, data+2, *(short *)data);
            #ifdef S4DATA_ALIGN
               memcpy(&templong, (data+2+*(short *)data), sizeof(long) );
               f4assignLong(openRecordWidth, templong);
               memcpy(&tempshort, (data+6+*(short *)data), sizeof(short) );
               f4assignInt(openNumFields, tempshort);
            #else
               f4assignLong(openRecordWidth, *(long *)(data+2+ *(short *)data));
               f4assignInt(openNumFields, *(short *)(data+6+ *(short *)data));
            #endif
            rc = d4append(openDbf);
            if (rc == r4unique)
            {
               outMess[0] = "\r\n\r\nLog file error! DBF identifier already used.\r\n\r\n";
               util4out(c4, stdout, outMess, 1);
            }
            break;

         case TRAN4CLOSE:
            util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
            util4rightJ(ltoa(tran4serverDataId(t4), client+11, 10), 12);
            rc = d4seek(openDbf, client);
            if (rc != 0)
            {
               if (rc == r4after || rc == r4eof)
               {
                  outMess[0] = "\r\n\r\nLog file error! DBF closed that is not open.\r\n\r\n";
                  util4out(c4, stdout, outMess, 1);
                  rc = -1;
               }
               break;
            }
            d4delete(openDbf);
            break;

         case TRAN4START:
            util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
            rc = d4seek(openDbf, client);
            if (rc != 0)
            {
               if (rc == r4after || rc == r4eof)
               {
                  outMess[0] = "\r\n\r\nLog file error! Transaction started by user that is not initialized.\r\n\r\n";
                  util4out(c4, stdout, outMess, 1);
                  rc = -1;
               }
               break;
            }
            rc = d4appendStart(tranDbf, 0);
            if (rc != 0)
               break;
            d4blank(tranDbf);
            f4assignLong(tranTranId, 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:
         case TRAN4ROLLBACK:
            util4rightJ(ltoa(tran4id(t4), client, 10), 12);
            rc = d4seek(tranDbf, client);
            if (rc != 0)
            {
               if (rc == r4after || rc == r4eof)
               {
                  if (tran4type(t4) == TRAN4COMPLETE)
                     outMess[0] = "\r\n\r\nLog file error! Transaction completed that was not started.\r\n\r\n";
                  else
                     outMess[0] = "\r\n\r\nLog file error! Transaction rolledback 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)
      if (t4->pos+sizeof(LOG4HEADER) != logFileLen)
         rc = file4lenSet(&c4->transFile.file, t4->pos+sizeof(LOG4HEADER));
      else
         rc = 0;
   return rc;
}

/****************************************************************************
**::  Function: checkDbfHeader
**
*****************************************************************************
**
**    Function: checkDbfHeader()
**
****************************************************************************/
short checkDbfHeader(CODE4 *c4, char *fileName)
{
   short rc, rc2;
   FILE4 file;
   DATA4HEADER_FULL fullHeader;
   S4LONG lengthRecords;

   c4trimN(fileName, strlen(fileName)+1);
   rc = file4open(&file, c4, fileName, 1);
   if (rc != 0)
      if (rc != r4noOpen)
         return(rc);
      else
         return(0);

   rc = file4readAll(&file, 0L, &fullHeader, sizeof(fullHeader));
   if (rc < 0)
   {
      file4close(&file);
      return(rc);
   }
   #ifdef S4BYTE_SWAP
      fullHeader.numRecs = x4reverseLong( (void *)&fullHeader.numRecs ) ;
      fullHeader.headerLen = x4reverseShort( (void *)&fullHeader.headerLen ) ;
      fullHeader.recordLen = x4reverseShort( (void *)&fullHeader.recordLen ) ;
   #endif
   lengthRecords = (file4len(&file) - fullHeader.headerLen) / fullHeader.recordLen;
   if (fullHeader.numRecs != lengthRecords)
      rc = file4write(&file, 4L, &lengthRecords, sizeof(S4LONG));
   rc2 = file4close(&file);
   if (rc != 0)
      rc2 = rc;
   return(rc2);
}

/****************************************************************************
**::  Function: fixIncompleteTran
**
*****************************************************************************
**
**    Function: fixIncompleteTran()
**
****************************************************************************/
short fixIncompleteTran(CODE4 *c4, DATA4 *openDbf, DATA4 *tranDbf, short force)
{
   short rc, rc2, rc3, i, curNumFields, consistency, intPart, decPart;
   unsigned smallerSize;
   TRAN4 *t4=0;
   DATA4 **userDbfs=0, *curDbf=0;
   char *outMess[8], *data=0, client[23];
   char ltoaTemp[12];
   char ltoaTemp2[12];
   unsigned long offsetMemo, offset;
   long curRecWidth;
   long logFileLen;

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

   t4 = &c4->c4trans.trans;
   rc = d4pack(openDbf);
   if (rc != 0)
      return(rc);
   if (d4recCount(openDbf) > 0)
   {
      userDbfs = (DATA4 **)u4allocFree(c4, d4recCount(openDbf)*4);
      if (userDbfs == 0)
         return(-1);
   }
   c4->errOpen = 0;
   for (rc = d4top(openDbf); rc == 0; rc = d4skip(openDbf,1L))
   {
      if (f4long(d4field(openDbf, "SERVERDTID")) == 0)
      {
         userDbfs[d4recNo(openDbf)-1L] = 0;
         continue;
      }
      rc = checkDbfHeader(c4, f4str(d4field(openDbf, "PHYSNAME")));
      if (rc != 0)
      {
         outMess[0] = "\r\n\r\nError! Can not fix record count for ";
         outMess[1] = f4str(d4field(openDbf, "PHYSNAME"));
         outMess[2] = "\r\n\r\n";
         util4out(c4, stdout, outMess, 3);
         curDbf = 0;
         c4->errorCode = 0;
      }
      else
         curDbf = d4open(c4, f4str(d4field(openDbf, "PHYSNAME")));
      if (curDbf && ((unsigned)f4long(d4field(openDbf, "RECORDWID")) != d4recWidth(curDbf) || f4int(d4field(openDbf, "NUMFIELDS")) != d4numFields(curDbf)))
      {
         rc = d4close(curDbf);
         if (rc != 0)
            c4->errorCode = 0;
         else
            c4->errorCode = r4noOpen;
         curDbf = 0;
      }
      if (curDbf != 0)
      {
         rc = d4reindex(curDbf);
         if (rc != 0)
         {
            d4close(curDbf);
            curDbf = 0;
            c4->errorCode = 0;
         }
      }
      if (curDbf == 0)
         if (c4->errorCode == r4noOpen)
         {
            outMess[0] = "\r\n\r\n";
            outMess[1] = f4str(d4field(openDbf, "PHYSNAME"));
            c4trimN(outMess[1], strlen(outMess[1])+1);
            outMess[2] = "\r\neither not found or structure does not match log file entry, skipping.\r\n\r\n";
            #ifndef S4WINDOWS
               outMess[3] = statusStringBlank;
               util4out(c4, stdout, outMess, 4);
            #else
               util4out(c4, stdout, outMess, 3);
            #endif
         }
         else
         {
            for (rc = d4skip(openDbf, -1L); rc == 0; rc = d4skip(openDbf, -1L))
               if (userDbfs[d4recNo(openDbf)-1L] != 0)
                  d4close(userDbfs[d4recNo(openDbf)-1L]);
            u4free(userDbfs);
            return(-1);
         }
      userDbfs[d4recNo(openDbf)-1L] = curDbf;
   }
   c4->errOpen = 1;

   for (rc = tran4bottom(t4); ; rc = tran4skip(t4, TRAN4BACKWARDS))
   {
      if (rc != 0)
         break;
      consistency = 1;
      intPart = (short)((unsigned long)(logFileLen-c4->c4trans.trans.pos)*50/logFileLen+50);
      decPart = (short)((unsigned long)(logFileLen-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 TRAN4START:
         case TRAN4COMPLETE:
         case TRAN4ROLLBACK:
         case TRAN4PACK:
         case TRAN4ZAP:
         case TRAN4VOID:
            break;

         case TRAN4WRITE:
            util4rightJ(ltoa(tran4id(t4), client, 10), 12);
            rc = d4seek(tranDbf, client);
            if (rc != 0)
            {
               if (rc == r4after || rc == r4eof)
                  rc = 0;
               break;
            }

            util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
            util4rightJ(ltoa(tran4serverDataId(t4), client+11, 10), 12);
            rc = d4seek(openDbf, client);
            if (rc != 0)
            {
               if (rc == r4after || rc == r4eof)
               {
                  outMess[0] = "\r\n\r\nLog file error! Write to DBF that is not open.\r\n\r\n";
                  util4out(c4, stdout, outMess, 1);
                  rc = -1;
               }
               break;
            }

            curDbf = userDbfs[d4recNo(openDbf)-1L];
            if (curDbf == 0)
               break;
            data = (char *)tran4getData(t4, 0L);
            if (data == 0)
            {
               rc = -1;
               break;
            }
            c4->errGo = 0;
            rc = d4go(curDbf, *(long *)data);
            c4->errGo = 1;
            if (rc != 0)
            {
               if (rc == r4entry)
               {
                  outMess[0] = "\r\n\r\n";
                  outMess[1] = f4str(d4field(openDbf, "PHYSNAME"));
                  c4trimN(outMess[1], strlen(outMess[1])+1);
                  outMess[2] = " consistency error!\r\nIn transaction #";
                  outMess[3] = ltoa(tran4id(t4), ltoaTemp, 10);
                  outMess[4] = ", write to DBF record #";
                  outMess[5] = ltoa(*(long *)data, ltoaTemp2, 10);
                  outMess[6] = " that does not exist, skipping.\r\n\r\n";
                  outMess[7] = statusStringBlank;
                  util4out(c4, stdout, outMess, 8);
                  rc = 0;
               }
               break;
            }
            curRecWidth = f4long(d4field(openDbf, "RECORDWID"));
            curNumFields = f4int(d4field(openDbf, "NUMFIELDS"));

            if (*(data+4+curRecWidth) == '*')
            {
               if (!d4deleted(curDbf))
                  consistency = 0;
            }
            else
               if (d4deleted(curDbf))
                  consistency = 0;
            offset = 5 + curRecWidth;
            offsetMemo = 4 + 2*curRecWidth;
            for (i=1; i <= d4numFields(curDbf) && consistency == 1; i++)
            {
               if (f4type(d4fieldJ(curDbf, i)) == 'M')
               {
                  offsetMemo += 4 + *(unsigned long *)(data+offsetMemo);
                  smallerSize = f4memoLen(d4fieldJ(curDbf, i)) < *(unsigned *)(data+offsetMemo) ? f4memoLen(d4fieldJ(curDbf, i)) : *(unsigned *)(data+offsetMemo);
                  if (strncmp(f4memoPtr(d4fieldJ(curDbf, i)), data+offsetMemo+4, smallerSize) != 0)
                     consistency = 0;
                  offsetMemo += 4 + *(unsigned long *)(data+offsetMemo);
                  offset += 10;
               }
               else
               {
                  if (strncmp(f4ptr(d4fieldJ(curDbf, i)), data+offset, f4len(d4fieldJ(curDbf, i))) != 0)
                     consistency = 0;
                  offset += f4len(d4fieldJ(curDbf, i));
               }
            }
            if (consistency == 0 && force == 0)
               break;

            if (*(data+4) == '*')
               d4delete(curDbf);
            else
               d4recall(curDbf);
            offset = 5;
            offsetMemo = 4 + 2*curRecWidth;
            for (i=1; i <= d4numFields(curDbf); i++)
            {
               if (f4type(d4fieldJ(curDbf, i)) == 'M')
               {
                  if (*(unsigned long *)(data+offsetMemo) != 0)
                  {
                     f4memoAssignN(d4fieldJ(curDbf, i), data+offsetMemo+4, *(unsigned *)(data+offsetMemo));
                     offsetMemo += 4 + *(unsigned long *)(data+offsetMemo);
                  }
                  else
                  {
                     offsetMemo += 4 + *(unsigned long *)(data+offsetMemo);
                     if (*(unsigned long *)(data+offsetMemo) != 0)
                        f4memoAssignN(d4fieldJ(curDbf, i), 0, 0);
                  }
                  offsetMemo += 4 + *(unsigned long *)(data+offsetMemo);
                  offset += 10;
               }
               else
               {
                  f4assignN(d4fieldJ(curDbf, i), data+offset, f4len(d4fieldJ(curDbf, i)));
                  offset += f4len(d4fieldJ(curDbf, i));
               }
            }
            break;

         case TRAN4APPEND:
            util4rightJ(ltoa(tran4id(t4), client, 10), 12);
            rc = d4seek(tranDbf, client);
            if (rc != 0)
            {
               if (rc == r4after || rc == r4eof)
                  rc = 0;
               break;
            }

            util4rightJ(ltoa(tran4clientId(t4), client, 10), 12);
            util4rightJ(ltoa(tran4serverDataId(t4), client+11, 10), 12);
            rc = d4seek(openDbf, client);
            if (rc != 0)
            {
               if (rc == r4after || rc == r4eof)
               {
                  outMess[0] = "\r\n\r\nLog file error! Append to DBF that is not open.\r\n\r\n";
                  util4out(c4, stdout, outMess, 1);
                  rc = -1;
               }
               break;
            }

            curDbf = userDbfs[d4recNo(openDbf)-1L];
            if (curDbf == 0)
               break;
            data = (char *)tran4getData(t4, 0L);
            if (data == 0)
            {
               rc = -1;
               break;
            }
            if (*(long *)data != d4recCount(curDbf))
            {
               outMess[0] ="\r\n\r\n";
               outMess[1] = f4str(d4field(openDbf, "PHYSNAME"));
               c4trimN(outMess[1], strlen(outMess[1])+1);
               outMess[2] = " consistency error!\r\nIn transaction #";
               outMess[3] = ltoa(tran4id(t4), ltoaTemp, 10);
               outMess[4] = ", appended DBF record #";
               outMess[5] = ltoa(*(long *)data, ltoaTemp2, 10);
               outMess[6] = " is no longer the last record, skipping.\r\n\r\n";
               outMess[7] = statusStringBlank;
               util4out(c4, stdout, outMess, 8);
               rc = 0;
               break;
            }
            c4->errGo = 0;
            rc = d4go(curDbf, *(long *)data);
            c4->errGo = 1;
            if (rc != 0)
               break;
            curRecWidth = f4long(d4field(openDbf, "RECORDWID"));
            curNumFields = f4int(d4field(openDbf, "NUMFIELDS"));

            if (*(data+4) == '*')
            {
               if (!d4deleted(curDbf))
                  consistency = 0;
            }
            else
               if (d4deleted(curDbf))
                  consistency = 0;
            offset = 5;
            offsetMemo = 4 + curRecWidth;
            for (i=1; i <= d4numFields(curDbf) && consistency == 1; i++)
            {
               if (f4type(d4fieldJ(curDbf, i)) == 'M')
               {
                  smallerSize = f4memoLen(d4fieldJ(curDbf, i)) < *(unsigned *)(data+offsetMemo) ? f4memoLen(d4fieldJ(curDbf, i)) : *(unsigned *)(data+offsetMemo);
                  if (strncmp(f4memoPtr(d4fieldJ(curDbf, i)), data+offsetMemo+4, smallerSize) != 0)
                     consistency = 0;
                  offsetMemo += 4 + *(unsigned long *)(data+offsetMemo);
                  offset += 10;
               }
               else
               {
                  if (strncmp(f4ptr(d4fieldJ(curDbf, i)), data+offset, f4len(d4fieldJ(curDbf, i))) != 0)
                     consistency = 0;
                  offset += f4len(d4fieldJ(curDbf, i));
               }
            }
            if (consistency == 0 && force == 0)
               break;

            code4unlockAutoSet(c4, 0);
            rc = d4unappend(curDbf);
            code4unlockAutoSet(c4, 1);
            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 (consistency == 0)
      {
         outMess[0] ="\r\n\r\n";
         outMess[1] = f4str(d4field(openDbf, "PHYSNAME"));
         c4trimN(outMess[1], strlen(outMess[1])+1);
         outMess[2] = " consistency error!\r\nIn transaction #";
         outMess[3] = ltoa(tran4id(t4), ltoaTemp, 10);
         outMess[4] = ", DBF record #";
         outMess[5] = ltoa(*(long *)data, ltoaTemp2, 10);
         if (force == 0)
            outMess[6] = " does not match log file entry, skipping.\r\n";
         else
            outMess[6] = " does not match log file entry, restore forced anyways.\r\n\r\n";
         outMess[7] = statusStringBlank;
         util4out(c4, stdout, outMess, 8);
      }
      if ( rc != 0 )
         break ;
   }
   if (rc == r4bof)
   {
      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;
   rc3 = 0;
   if (rc == 0)
   {
      for (rc=d4top(tranDbf); rc == 0; rc = d4skip(tranDbf, 1L))
      {
         rc = tran4set(t4, r4off, f4long(d4field(tranDbf, "TRANID")), 0, TRAN4ROLLBACK, 0, 0, 0);
         if (rc < 0)
         {
            rc2 = rc;
            continue;
         }
         rc = tran4lowAppend(t4, 0);
         if (rc < 0)
            rc2 = rc;
      }
      if (rc != r4eof)
         rc2 = rc;
   }

   for (rc=d4bottom(openDbf); rc == 0; rc = d4skip(openDbf, -1L))
   {
      if (rc2 == 0)
      {
         if (f4long(d4field(openDbf, "SERVERDTID")) == 0)
         {
            if (rc3 == 0)
               rc = tran4set(t4, r4off, 0, f4long(d4field(openDbf, "CLIENTID")), TRAN4INIT_UNDO, 0, 0, 0);
         }
         else
            rc = tran4set(t4, r4off, 0, f4long(d4field(openDbf, "CLIENTID")), TRAN4CLOSE, 0, f4long(d4field(openDbf, "CLIENTDTID")), f4long(d4field(openDbf, "SERVERDTID")));
         if (rc < 0)
         {
            rc3 = rc;
            continue;
         }
         rc = tran4lowAppend(t4, 0);
         if (rc < 0)
            rc3 = rc;
      }
      if (userDbfs[d4recNo(openDbf)-1L] != 0)
         rc2 = rc2 || d4close(userDbfs[d4recNo(openDbf)-1L]);
   }
   if (rc == r4bof || rc == r4eof)
      rc = 0;

   if (!rc && !rc2 && !rc3)
   {
      tran4set(t4, r4off, 0, 0, TRAN4SHUTDOWN, 0, 0, TRAN4VERSION_NUM);
      tran4lowAppend(t4, 0);
   }
   return(rc || rc2 || rc3);
}

/****************************************************************************
**::  Function: getLogFileName
**
*****************************************************************************
**
**    Function: getLogFileName()
**
****************************************************************************/
int getLogFileName(CODE4 *c4, UTIL4FLAG *u4flag, char **logFileName)
{
   #ifdef S4UTIL_CONCURRENCY
      int rc;
      DATA4 *configDbf=0;
      char *outMess[1];

      if (u4flags[UTIL4CONFIG].ptr != 0)
         configDbf = d4open(c4, u4flags[UTIL4CONFIG].ptr[0]);
      else
         configDbf = d4open(c4, "S4SERVER");
      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\r\n";
            util4out(c4, stdout, outMess, 1);
         }
      }
      rc = rc || d4close(configDbf);
      return(rc);
   #else
      *logFileName = u4flags[UTIL4LOG].ptr[0] ;
      return 0 ;
   #endif
}

/****************************************************************************
**::  Function: main
**
*****************************************************************************
**
**    Function: main()
**
****************************************************************************/
#ifdef S4DLL_BUILD
int S4FUNCTION  mainLog4Fix(int argc, char *argv[], int hWnd )
#else
int main(int argc, char *argv[])
#endif
{
   CODE4 c4;
   DATA4 *openDbf=0, *tranDbf=0;
   char *logFileName=0, *outMess[2];
   int rc=0, goodAlready=0;
   short force;

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

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

   code4init(&c4);
   c4.lockAttempts = WAIT4EVER;
   c4.accessMode = OPEN4DENY_RW;
   #ifdef N4OTHER
      c4.autoOpen = 0;
   #endif
   #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);
   force = util4checkOnOffCommandLine(&c4, u4flags, UTIL4FORCE, 0);
   if (force == -1)
      rc = -1;
   #ifdef S4UTIL_CONCURRENCY
      if (rc == 0 && u4flags[UTIL4CONFIG].found == 0)
      {
         outMess[0] = "Command line error! Must specify -config switch.\r\n\r\n";
   #else
      if (rc == 0 && u4flags[UTIL4LOG].found == 0)
      {
         outMess[0] = "Command line error! Must specify -log switch.\r\n\r\n";
   #endif
         util4out(&c4, stdout, outMess, 1);
         rc = -1;
      }

   if (rc != 0)
   {
      util4printCommandLine(&c4, u4flags, u4flagsNum, argv[0]);
      util4flagsFree(u4flags, u4flagsNum);
      code4initUndo(&c4);
      #ifdef S4WINDOWS
         sendCloseMessage( hWndStatus ) ;
      #endif
      return(rc);
   }

   rc = getLogFileName(&c4, u4flags, &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)
   {
      openDbf = d4createTemp(&c4, openFields, openTags);
      tranDbf = d4createTemp(&c4, tranFields, tranTags);
      if (openDbf == 0 || tranDbf == 0)
         rc = -1;
   }
   c4.lockAttempts = 1;
   if (rc == 0)
   {
      rc = code4tranLockTransactions(&c4.c4trans, TRAN4LOCK_FIX);
      if (rc == r4locked)
      {
         outMess[0] = "Concurrency error! Another copy of this utility is running!\r\n\r\n";
         util4out(&c4, stdout, outMess, 1);
      }
   }
   if (rc == 0)
   {
      rc = code4tranLockTransactions(&c4.c4trans, TRAN4LOCK_BACKUP);
      if (rc == r4locked)
      {
         outMess[0] = "Concurrency error! Backup utility is running!\r\n\r\n";
         util4out(&c4, stdout, outMess, 1);
      }
   }
   if (rc == 0)
   {
      rc = code4tranLockTransactions(&c4.c4trans, TRAN4LOCK_SERVER);
      if (rc == r4locked)
      {
         outMess[0] = "Concurrency error! The server is running!\r\n\r\n";
         util4out(&c4, stdout, outMess, 1);
      }
   }
   c4.lockAttempts = WAIT4EVER;
   if (rc == 0)
   {
      rc = tran4top(&c4.c4trans.trans);
      if (rc == 0)
         if (tran4type(&c4.c4trans.trans) != TRAN4SHUTDOWN || tran4serverDataId(&c4.c4trans.trans) > TRAN4VERSION_NUM)
         {
            outMess[0] = "Log file error! File is either a backup file or a newer version than this utility can handle!\r\n\r\n";
            util4out(&c4, stdout, outMess, 1);
            rc = -1;
         }
   }

   if (rc == 0)
      rc = findIncompleteTran(&c4, openDbf, tranDbf);
   if (rc == 0)
   {
      rc = tran4bottom(&c4.c4trans.trans);
      if (rc == 0)
         if (tran4type(&c4.c4trans.trans) == TRAN4SHUTDOWN)
         {
            #ifndef S4WINDOWS
               outMess[0] = "\r\n";
               util4out(&c4, stderr, outMess, 1);
               util4out(&c4, stdout, outMess, 1);
            #endif
            outMess[0] = "Log file already in correct condition!\r\n\r\n";
            util4out(&c4, stdout, outMess, 1);
         }
         else
            rc = fixIncompleteTran(&c4, openDbf, tranDbf, force);
   }
   if (rc != 0)
      outMess[0] = "Error! Utility did not successfully finish!\r\n";
   else
   {
      #ifdef S4WINDOWS
         messageType = MB_ICONEXCLAMATION ;
         sendStatusMessage( hWndStatus, 100 ) ;
      #endif
      outMess[0] = "Utility successfully finished!\r\n";
   }
   util4out(&c4, stdout, outMess, 1);

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