/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*		1993-2008, All Rights Reserved 				*/
/*									*/
/* MODULE NAME : slogfil.c						*/
/* PRODUCT(S)  : SLOG							*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 03/27/08  EJV     22    Elim warning from previous changes.		*/
/* 03/27/08  EJV     21    Use S_MAX_PATH instead of MAX_PATH.		*/
/*                         slog_get_index_file_name: added destLen param*/
/*                         and changed to return result.		*/
/* 10/08/07  EJV     20    Moved MAX_PATH define to sysincs.h		*/
/* 01/15/07  EJV     19    Chg S_LOCK_RESOURCES to S_LOCK_UTIL_RESOURCES*/
/* 02/13/06  DSF     18    Migrate to VS.NET 2005			*/
/* 10/31/05  MDE     17    Tweaked index tile name creation		*/
/* 09/23/05  MDE     16    Tweaked size of idxFileName in     		*/
/* 09/14/05  DSF     15    Increased size of idxFileName in     	*/
/* 08/26/05  MDE     14    Fixed startup w/wo 				*/
/* 08/10/05  MDE     13    Added index file, wrap cleanup, etc.		*/
/* 06/07/04  EJV     12    slogFil: chg state if reopen err (HARD_FLUSH)*/
/* 05/13/04  EJV     11    Moved log header formatting to slogSetHdr.	*/
/* 03/11/04  GLB     10    Remove "thisFileName"                    	*/
/* 02/20/03  JRB     09    Del PSOS code.				*/
/* 10/13/00  EJV     08    Moved glbsem.h below sysincs.h.		*/
/* 08/22/00  KCR     07    Added FIL_CTRL_NO_LOG_HDR			*/
/* 09/13/99  MDE     06    Added SD_CONST modifiers			*/
/* 04/14/99  MDE     05    Removed unnecessary include files		*/
/* 10/23/98  MDE     04    No blank line bet. logs if LOG_NO_HEADER_CR	*/
/* 10/16/98  DSF     03    Spelling					*/
/* 10/08/98  MDE     02    Migrated to updated SLOG interface		*/
/* 10/06/97  DSF     01    Added thisFileName				*/
/* 04/02/97  DTL   7.00    MMSEASE 7.0 release. See MODL70.DOC for	*/
/*			   history.					*/
/************************************************************************/

#if defined (_WIN32)
#pragma warning(disable : 4996)
#endif

#include "glbtypes.h"
#include "sysincs.h"
#include "glbsem.h"
#include <errno.h>
#if defined(_WIN32)
#include "io.h"      
#endif

#include "slog.h"

/************************************************************************/

/* Do not change w/o testing */
#define OLDEST_STRING  "          OLDEST DATA STARTS BELOW"

/************************************************************************/
/* Local function prototypes						*/
/************************************************************************/

static ST_VOID  printWipe 	      (FILE *fh);
static ST_VOID  printWrap        (FILE *fh);
static ST_VOID  printStart       (FILE *fh);
static ST_VOID  slogFileErrorPrint(ST_CHAR *text, ST_CHAR *fname);
static ST_LONG  findOldestMessage(LOG_CTRL	*lc, FILE *fh);
static ST_INT copy_named_files (LOG_CTRL *lc, SD_CONST ST_CHAR *SD_CONST dest, 
		      SD_CONST ST_CHAR *SD_CONST src);
static ST_INT copy_named_files_ex (LOG_CTRL *lc, SD_CONST ST_CHAR *SD_CONST dest, 
	SD_CONST ST_CHAR *SD_CONST src);
static ST_VOID chk_slog_service (LOG_CTRL	*lc);

/************************************************************************/
/*                              slogFile				*/
/************************************************************************/

ST_VOID slogFile (LOG_CTRL *lc, SD_CONST ST_INT logType, 
		SD_CONST ST_CHAR *SD_CONST logTypeStr, 
		SD_CONST ST_CHAR *SD_CONST sourceFile, 
		SD_CONST ST_INT lineNum, 
		SD_CONST ST_INT bufLen, SD_CONST ST_CHAR *buf)

  {
FILE *fh;
struct stat fileStat;
ST_LONG seekPos;
ST_LONG currPos;
ST_ULONG fileSize;
ST_CHAR slogHdr[SLOG_MAX_HDR + 1];  
/*ST_INT wrap;								*/

  if (!(lc->fc.state & FIL_STATE_OPEN))	/* if not already opened	*/
    {
  /* Need to open the file						*/
    if (lc->fc.ctrl & FIL_CTRL_NO_APPEND)  /* If overwrite is requested	*/
      fh = NULL;
    else
      lc->fc.fp = fh = fopen (lc->fc.fileName,"r+");

    if (!fh)		/* File must not exist, or overwrite requested	*/
      {			/* try creating it				*/
      lc->fc.fp = fh = fopen (lc->fc.fileName,"w+");
      if (!fh)
        {
        slogFileErrorPrint ("OPEN",lc->fc.fileName);
        return;
        }
      }

  /* We have successfully opened the log file */
    lc->fc.state = FIL_STATE_OPEN;
    if (lc->fc.ctrl & FIL_CTRL_SETBUF_EN)  /* If setbuf desired		*/
      setbuf (fh,NULL);  		  /* no buffering, please	*/

  /* If appending, need to get to the start write position */
    if ((lc->fc.ctrl & FIL_CTRL_NO_APPEND) == 0)
      {
  /* Check the current file size, see if we can just append */
      if (fstat (fileno(fh), &fileStat))
        {
        slogFileErrorPrint ("SEEK",lc->fc.fileName);
        return;
	}
      fileSize = fileStat.st_size;
      if (fileSize < lc->fc.maxSize)
        seekPos = (ST_LONG) fileSize;
      else
        {
        seekPos = findOldestMessage (lc, fh);
        lc->fc.state |= FIL_STATE_NEED_WIPE; /* flag to show we wrapped */
	}
      if (fseek (fh,seekPos,SEEK_SET))
        {
        slogFileErrorPrint ("SEEK",lc->fc.fileName);
        return;
        }
      }

/* Print the 'started' message						*/
    if (!(lc->fc.ctrl & FIL_CTRL_NO_LOG_HDR)) /* log file header */
      printStart (fh);
    }/* File not opened							*/
  else 
    { 	/* The file has already been opened for logging			*/
    fh = lc->fc.fp;
    if (lc->fc.ctrl & FIL_CTRL_WRAP_EN) /* if wrap allowed		*/
      {    

/* Check to see the position of the file to see if we need to wrap it	*/

      currPos = ftell (fh);
      if (currPos == -1L)
        {
        slogFileErrorPrint ("TELL",lc->fc.fileName);
        return;
        }

/* Check to see if we need to wrap - currPos is where we would write	*/

      if (((ST_ULONG) currPos > lc->fc.maxSize))
        {

/* Yep, need to wrap - cut off the rest of the file to avoid trailing 	*/

#if defined(MSDOS) || defined(__OS2__) || defined(_WIN32)
        if (chsize (fileno(fh), currPos))
  	  {
          slogFileErrorPrint ("CHSIZE",lc->fc.fileName);
          }
#else
	 #ifndef __ECOS
        if (ftruncate (fileno(fh), currPos))
	  {
          slogFileErrorPrint ("FTRUNCATE",lc->fc.fileName);
	  }
		#endif
#endif

/* Seek to start of file						*/
        if (fseek (fh,0L,SEEK_SET))
          {
          slogFileErrorPrint ("SEEK",lc->fc.fileName);
          return;
          }

/* Print wrap message at top						*/

        seekPos = 0;
        printWrap (fh);
        lc->fc.state |= FIL_STATE_NEED_WIPE; /* flag to show we wrapped */
        } /* need to wrap */
      } /* Wrap not disabled */
    } /* File already opened	*/

/* Ok, the file is open and the pointer set to where we log the next	*/
/* message, any wrap has been printed - time to log the info		*/

/* Now print the message header						*/
  slogSetHdr (lc, logType, logTypeStr, sourceFile, lineNum, slogHdr, "\n");
  fprintf (fh,"%s", slogHdr);

/* Now print the message buffer						*/
  fprintf (fh,"%s", buf);

/* Now print 'wipe bar', if file has wrapped some time in the past	*/
/* and if we are supposed to do the wipe				*/

  if (lc->fc.state & FIL_STATE_NEED_WIPE && 
      lc->fc.ctrl & FIL_CTRL_WIPE_EN)
    {
    currPos = ftell (fh);
    if (currPos == -1L)
      {
      slogFileErrorPrint ("TELL",lc->fc.fileName);
      return;
      }

/* Print the wipe bar							*/
    lc->fc.wipeFilePos = currPos;
    printWipe (fh);

/* Seek to the current write position					*/

    if (fseek (fh,currPos,SEEK_SET))
      {
      slogFileErrorPrint ("SEEK",lc->fc.fileName);
      return;
      }
    } /* Need to print wipe bar						*/

/* Try to not lose log data						*/

  fflush (fh);

/* Check for paranoid user - wants to close && reopen			*/

  if (lc->fc.ctrl & FIL_CTRL_HARD_FLUSH)
    {
    currPos = ftell (fh);
    if (currPos == -1L)
      {
      slogFileErrorPrint ("TELL",lc->fc.fileName);
      return;
      }

/* Close the file							*/

    fclose (fh);

/* Re-open the file							*/

    lc->fc.fp = fh = fopen (lc->fc.fileName,"r+");
    if (!fh)		/* better exist					*/
      {		
      slogFileErrorPrint ("OPEN",lc->fc.fileName);
      lc->fc.state &= ~FIL_STATE_OPEN;
      return;
      }

    if (lc->fc.ctrl & FIL_CTRL_SETBUF_EN)  /* If setbuf desired		*/
      setbuf (fh,NULL);  		  /* no buffering, please	*/

/* Seek to current write position					*/

    if (fseek (fh,currPos,SEEK_SET))
      {
      slogFileErrorPrint ("SEEK",lc->fc.fileName);
      return;
      }
    } /* if hard flush enabled						*/
  }

/************************************************************************/
/* 		              slogCloseFile				*/
/************************************************************************/

ST_VOID slogCloseFile (LOG_CTRL *lc)
  {

  S_GS_INIT ();
  S_LOCK_UTIL_RESOURCES ();

  if (lc->fc.state & FIL_STATE_OPEN)
    {
    if (fclose (lc->fc.fp))
      {
      fprintf (stderr,"\n *** LOG FILE CLOSE PROBLEM (%s) : ",
      	lc->fc.fileName);
      return;
      }
    lc->fc.state &= ~FIL_STATE_OPEN;
    }
  S_UNLOCK_UTIL_RESOURCES ();
  }

/************************************************************************/
/*		               slogCloneFile				*/
/************************************************************************/

ST_VOID slogCloneFile (LOG_CTRL *lc, SD_CONST ST_CHAR *newFile)
  {
ST_LONG 		currPos;
FILE 		*fh;

  S_GS_INIT ();
  S_LOCK_UTIL_RESOURCES ();

  fh = lc->fc.fp;
  if (lc->fc.state & FIL_STATE_OPEN)
    {
    currPos = ftell (fh);
    if (currPos == -1L)
      {
      slogFileErrorPrint ("TELL",lc->fc.fileName);
      S_UNLOCK_UTIL_RESOURCES ();
      return;
      }

/* Close the file							*/
    fclose (fh);

/* Copy the log file							*/
   copy_named_files (lc, newFile, lc->fc.fileName);
   
/* Re-open the file at the old position					*/
    lc->fc.fp = fh = fopen (lc->fc.fileName,"r+");
    if (!fh)		/* better exist					*/
      {		
      slogFileErrorPrint ("OPEN",lc->fc.fileName);
      S_UNLOCK_UTIL_RESOURCES ();
      return;
      }

    if (lc->fc.ctrl & FIL_CTRL_SETBUF_EN)  /* If setbuf desired		*/
      setbuf (fh,NULL);  		  /* no buffering, please	*/

/* Seek to current write position					*/
    if (fseek (fh,currPos,SEEK_SET))
      {
      slogFileErrorPrint ("SEEK",lc->fc.fileName);
      S_UNLOCK_UTIL_RESOURCES ();
      return;
      }
    }
  else	/* log file not opened, just copy it				*/
    copy_named_files (lc, newFile, lc->fc.fileName);

  S_UNLOCK_UTIL_RESOURCES ();
  }

/************************************************************************/
/*		               slogCloneFileEx				*/
/************************************************************************/

ST_VOID slogCloneFileEx (LOG_CTRL *lc, SD_CONST ST_CHAR *newFile)
  {
ST_LONG 		currPos;
FILE 		*fh;

  S_GS_INIT ();
  S_LOCK_UTIL_RESOURCES ();

  fh = lc->fc.fp;
  if (lc->fc.state & FIL_STATE_OPEN)
    {
    currPos = ftell (fh);
    if (currPos == -1L)
      {
      slogFileErrorPrint ("TELL",lc->fc.fileName);
      S_UNLOCK_UTIL_RESOURCES ();
      return;
      }

/* Close the file							*/
    fclose (fh);

/* Copy the log file							*/
   copy_named_files_ex (lc, newFile, lc->fc.fileName);
   
/* Re-open the file at the old position					*/
    lc->fc.fp = fh = fopen (lc->fc.fileName,"r+");
    if (!fh)		/* better exist					*/
      {		
      slogFileErrorPrint ("OPEN",lc->fc.fileName);
      S_UNLOCK_UTIL_RESOURCES ();
      return;
      }

    if (lc->fc.ctrl & FIL_CTRL_SETBUF_EN)  /* If setbuf desired		*/
      setbuf (fh,NULL);  		  /* no buffering, please	*/

/* Seek to current write position					*/
    if (fseek (fh,currPos,SEEK_SET))
      {
      slogFileErrorPrint ("SEEK",lc->fc.fileName);
      S_UNLOCK_UTIL_RESOURCES ();
      return;
      }
    }
  else	/* log file not opened, just copy it				*/
    copy_named_files_ex (lc, newFile, lc->fc.fileName);

  S_UNLOCK_UTIL_RESOURCES ();
  }

/************************************************************************/
/*		              slogDeleteFile				*/
/************************************************************************/

ST_VOID slogDeleteFile (LOG_CTRL *lc)
  {

  S_GS_INIT ();
  S_LOCK_UTIL_RESOURCES ();

/* first close the log file						*/

  slogCloseFile (lc);

/* Now delete it							*/

  remove (lc->fc.fileName);

  S_UNLOCK_UTIL_RESOURCES ();
  }

/************************************************************************/
/*			       findOldestMessage			*/
/************************************************************************/

static ST_LONG findOldestMessage (LOG_CTRL *lc, FILE *fh)
  {
ST_CHAR idxFileName[S_MAX_PATH];
FILE *idxFh;
ST_CHAR buf[200];
ST_LONG startPos;
ST_LONG currPos;
ST_INT count;
ST_INT cmpLen;

  cmpLen = strlen(OLDEST_STRING);

/* Need to find the start position by looking for the 'OLDEST' message. */
/* See if we have an index file and seek position ... */
  if (slog_get_index_file_name (lc, idxFileName, sizeof(idxFileName)) == SD_SUCCESS)
    {
    idxFh = fopen (idxFileName,"r");
    if (idxFh)
      {
      count = fscanf (idxFh, "%ld", &startPos);
      fclose (idxFh);
      if (count == 1)
        {
        if (fseek (fh, startPos, SEEK_SET) == 0)
          {
          fgets (buf,200,fh);
          fgets (buf,200,fh);
          fgets (buf,200,fh);
          if (fgets (buf,200,fh))
            {
            if (buf[10] == 'O' && !strncmp (buf,OLDEST_STRING, cmpLen))
              return (startPos);
            }
          }
        }
      }
    }

/* Darn, need to start at the top and find it */
  fseek (fh, 0, SEEK_SET);
  while (SD_TRUE)
    {
    currPos = ftell (fh);
    if (!fgets (buf,200,fh))
      break; 

    chk_slog_service (lc);

  /* Is this the position with the marker string? */
    if (buf[10] == 'O' && !strncmp (buf,OLDEST_STRING, cmpLen))
      return (currPos);
    }

/* No oldest found, go to the start of the file */
  fseek (fh, 0, SEEK_END);
  currPos = ftell (fh);
  return (currPos);
  }

/************************************************************************/
/*			slog_get_index_file_name			*/
/************************************************************************/

ST_RET slog_get_index_file_name (LOG_CTRL *lc, ST_CHAR *dest, ST_INT destLen)
  {
ST_CHAR *src;
ST_INT   srcLen;

  src = lc->fc.fileName;
  srcLen = strlen(src);

  if (!src || srcLen == 0)
    return (SD_FAILURE);
  if (!dest || destLen < (srcLen+4))	/* +4 for len of ".sli"	*/
    return (SD_FAILURE);

  while (SD_TRUE)
    {
    *dest = *src;
    if (*dest == 0)	/* End of string ... */
      break;
      
    if (*dest == '.') 
      *dest = '_';

    ++src; 
    ++dest;
    }

  strcat (dest, ".sli");
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                              printStart				*/
/************************************************************************/

static ST_VOID  printStart (FILE *fh)
  {
time_t t;

  t = time(NULL);
  fprintf (fh,"\n\n***********************************************************");
  fprintf (fh,"\n          LOGGING STARTED %s", ctime(&t));
  fprintf (fh,"***********************************************************");
  }

/************************************************************************/
/*                               printWrap                              */
/************************************************************************/

static ST_VOID  printWrap (FILE *fh)
  {
time_t t;

  t = time(NULL);
  fprintf (fh,"\n***********************************************************");
  fprintf (fh,"\n          FILE WRAPPED %s", ctime(&t));
  fprintf (fh,"***********************************************************");
  }

/************************************************************************/
/*                                printWipe				*/
/************************************************************************/

static ST_VOID  printWipe (FILE *fh)
  {
  fprintf (fh,"\n\n***********************************************************");
  fprintf (fh,"\n%s",OLDEST_STRING);;
  fprintf (fh,"\n***********************************************************\n\n");
  }

/************************************************************************/
/*			      slogFileErrorPrint			*/
/************************************************************************/

static ST_VOID slogFileErrorPrint (ST_CHAR *text, ST_CHAR *fname)
  {
  fprintf (stderr,"\n*** LOG FILE %s ERROR (%s) : ",text,fname);
  fprintf (stderr,"%s",strerror(errno));
  }

/************************************************************************/
/*                       copy_named_files					*/
/************************************************************************/

#define FILE_READ_SIZE 	1000

static ST_INT copy_named_files (LOG_CTRL *lc, 
      SD_CONST ST_CHAR *SD_CONST dest, SD_CONST ST_CHAR *SD_CONST src)
  {
FILE *dest_fp;
FILE *src_fp;
ST_INT ret;
ST_CHAR buf[FILE_READ_SIZE+1];
ST_INT bytes_read;
ST_INT bytes_written;

  if (!(src_fp = fopen (src,"rb")))
    return (SD_FAILURE);

  if (!(dest_fp = fopen (dest,"wb")))
    {
    fclose (src_fp);
    return (SD_FAILURE);
    }

  ret = SD_SUCCESS;
  while (SD_TRUE)
    {
    if (!(bytes_read = fread (buf,1,FILE_READ_SIZE,src_fp)))
      break;

    bytes_written = fwrite (buf,1,bytes_read,dest_fp);
    if (bytes_read != bytes_written)
      {
      ret = SD_FAILURE;
      break;
      }    

    chk_slog_service (lc);
    }    

  if (fclose (src_fp))
    ret = SD_FAILURE;

  if (fclose (dest_fp))
    ret = SD_FAILURE;

  return (ret);
  }


/************************************************************************/
/*                       copy_named_files_ex				*/
/************************************************************************/

static ST_INT copy_named_files_ex (LOG_CTRL *lc, SD_CONST ST_CHAR *SD_CONST dest, 
		SD_CONST ST_CHAR *SD_CONST src)
  {
FILE *dest_fp;
FILE *src_fp;
ST_INT ret;
ST_CHAR buf[200+1];
ST_LONG  currPos;
ST_LONG  oldestPos;

  if (!(src_fp = fopen (src,"rb")))
    return (SD_FAILURE);

  oldestPos = findOldestMessage (lc, src_fp);
  if (fseek (src_fp, oldestPos, SEEK_SET))
    {
    slogFileErrorPrint ("SEEK",lc->fc.fileName);
    fclose (src_fp);
    return (SD_FAILURE);
    }

  if (!(dest_fp = fopen (dest,"wb")))
    {
    fclose (src_fp);
    return (SD_FAILURE);
    }

  ret = SD_SUCCESS;
  while (SD_TRUE)
    {
    if (!fgets (buf,200,src_fp))
      break;

    fputs (buf,dest_fp);

    chk_slog_service (lc);
    }    

  if (fclose (src_fp))
    ret = SD_FAILURE;

  if (ret == SD_SUCCESS)
    {
    if (oldestPos > 0)
      {
      if (!(src_fp = fopen (src,"rb")))
        {
        fclose (dest_fp);
        return (SD_FAILURE);
	}

      currPos = 0;
      while (currPos < oldestPos)
        {
        if (!fgets (buf,200,src_fp))
          break;

        fputs (buf,dest_fp);

        currPos = ftell (src_fp);
        if (currPos == -1L)
          {
          slogFileErrorPrint ("TELL",lc->fc.fileName);
          fclose (src_fp);
          fclose (dest_fp);
          return (SD_FAILURE);
	  }

        chk_slog_service (lc);
        }
      }
    }

  if (fclose (src_fp))
    ret = SD_FAILURE;

  if (fclose (dest_fp))
    ret = SD_FAILURE;

  return (ret);
  }


/************************************************************************/
/*			chk_slog_service 				*/
/************************************************************************/

static ST_INT slog_service_count;

static ST_VOID chk_slog_service (LOG_CTRL *lc)
  {
ST_UINT32 logCtrlSave;

  /* Allow the application to do it's thing during a slow operation	*/
  if (slog_service_fun)
    {
    if (++slog_service_count > 10)
      {
      slog_service_count = 0;

  /* do not allow file logging during this operation		*/
      logCtrlSave = lc->logCtrl;
      lc->logCtrl &= ~LOG_FILE_EN;
      (*slog_service_fun) ();
      lc->logCtrl = logCtrlSave;
      } 
    } 
  }
