// ZFortify.cpp - A fortified shell for new and delete.

//     The  minimum  you  need  to  do for ZFortify is to define the symbol
// ZFORTIFY,  and  link  zfortify.o.   Each source file should also include
// "zfortify.hpp",  but  this  isn't strictly necessary.  If a file doesn't
// #include  "Fortify.hpp",  then it's allocations will still be fortified,
// however  you  will not have any source-code details in any of the output
// messages  (this will be the case for all libraries, etc, unless you have
// the source for the library and can recompile it with Fortify).
//     If  you  do  not  have  stdout  available,  you  may  wish to set an
// alternate  output  function,  or  turn  on  the  AUTOMATIC_LOGFILE.  See
// ZFortify_SetOutputFunc() and AUTOMATIC_LOGFILE, below.
// 
// None  of  the  functions  in  this  file should be called directly; they
// should be called through the macros defined in ZFortify.hpp.

#ifdef ZFORTIFY
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <ctype.h>

#define  ZFORTIFY_CPP   // So ZFortify.hpp knows to not define the ZFortify macros 
#include "ZFortify.hpp"

// the user's options 
#include "ufortify.hpp"   

struct Header
{
	char          *File;     // The sourcefile of the caller 
	unsigned long  Line;     // The sourceline of the caller
	size_t         Size;     // The size of the new'd block
	struct Header *Prev;     // List pointers
	struct Header *Next;     //
	int            Scope;    // Scope level this block was allocated in
	int            Checksum; // For validating the Header structure; see ChecksumHeader()
};

// Local prototypes
static int  CheckBlock(struct Header *h, char *file, unsigned long line);
static int  CheckFortification(unsigned char *ptr, unsigned char value, size_t size);
static void SetFortification(unsigned char *ptr, unsigned char value, size_t size);
static void OutputFortification(unsigned char *ptr, unsigned char value, size_t size);
static int  IsHeaderValid(struct Header *h);
static void MakeHeaderValid(struct Header *h);
static int  ChecksumHeader(struct Header *h);
static int  IsOnList(struct Header *h);
static void OutputMemory(struct Header *h);
static void OutputHeader(struct Header *h);
static void OutputLastVerifiedPoint(void);

// The default output function
static void st_DefaultOutput(const char *String)
{
	printf(String);
}

static struct Header *st_Head = 0; // Head of alloc'd memory list 
static ZFortify_OutputFuncPtr  st_Output = st_DefaultOutput; // Output function for errors 
static char st_Buffer[256];       // Temporary buffer for sprintf's 
static int st_Disabled = 0;       // If true, ZFortify is inactive 
static int st_NewFailRate = 0;    // % of the time to false fail new

static char          *st_LastVerifiedFile = "unknown";
static unsigned long  st_LastVerifiedLine = 0;

static char	         *st_Delete_File = 0;
static unsigned long  st_Delete_Line = 0;

static int            st_Scope = 0;

// operator new() - Allocates a block of memory, with extra bits for
//                  misuse protection/detection. 
// Features:
//     +  Adds the new'd memory onto ZFortify's own private list.
//        (With a checksum'd header to detect corruption of the memory list)
//     +  Places sentinals on either side of the user's memory with
//        known data in them, to detect use outside of the bounds
//        of the block
//     +  Initializes the new'd memory to some "nasty" value, so code
//        can't rely on it's contents.
//     +  Can check all sentinals on every NEW.
//     +  Can generate a warning message on a NEW fail.
//     +  Can randomly "fail" at a set fail rate
void *ZFORTIFY_STORAGE
operator new(size_t size) 
{ 
	return operator new(size, "new", 0); 
}

#ifdef ZFORTIFY_PROVIDE_ARRAY_NEW

void *ZFORTIFY_STORAGE
operator new[](size_t size) 
{ 
	return operator new(size, "new[]", 0); 
}

void *ZFORTIFY_STORAGE
operator new[](size_t size, char *file, unsigned long line)
{
	return operator new(size, file, line);
}

#endif // ZFORTIFY_PROVIDE_ARRAY_NEW

void *ZFORTIFY_STORAGE
operator new(size_t size, char *file, unsigned long line)
{
	unsigned char *ptr;
	struct Header *h;

	ZFORTIFY_LOCK();

	if(st_Disabled)
	{
		ptr = (unsigned char *)malloc(size);
		ZFORTIFY_UNLOCK();
		return(ptr);
	}

#ifdef CHECK_ALL_MEMORY_ON_NEW
	ZFortify_CheckAllMemory(file, line);
#endif  

	if(size == 0)
	{
#ifdef WARN_ON_ZERO_NEW
		sprintf(st_Buffer, "\nZFortify: %s.%ld\n         new(0) attempted failed\n",
						   file, line);
		st_Output(st_Buffer);
#endif

		ZFORTIFY_UNLOCK();
		return(0);       
	}

	if(st_NewFailRate > 0)
	{
		if(rand() % 100 < st_NewFailRate)
		{
#ifdef WARN_ON_FALSE_FAIL
			sprintf(st_Buffer, "\nZFortify: %s.%ld\n         new(%ld) \"false\" failed\n",
							   file, line, (unsigned long)size);
			st_Output(st_Buffer);
#endif
			ZFORTIFY_UNLOCK();
			return(0);
		}
	}
  
#ifdef WARN_ON_SIZE_T_OVERFLOW               
	{
		size_t private_size = sizeof(struct Header) 
							+ ZFORTIFY_BEFORE_SIZE + size + ZFORTIFY_AFTER_SIZE;

		If(private_size < size) /* Check to see if the added baggage is larger than size_t */
		{
			sprintf(st_Buffer, "\nZFortify: %s.%ld\n         new(%ld) has overflowed size_t.\n",
								file, line, (unsigned long)size);
			st_Output(st_Buffer);
			ZFORTIFY_UNLOCK();
			return(0);
		}                              
	}
#endif                              

	// new the memory, including the space for the header 
  	// and fortification buffers (well, we fake it with malloc).
	ptr = (unsigned char *)malloc(sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE + size + ZFORTIFY_AFTER_SIZE);
	if(!ptr)
	{
#ifdef WARN_ON_NEW_FAIL
		sprintf(st_Buffer, "\nZFortify: %s.%ld\n         new(%ld) failed\n",
							file, line, (unsigned long)size);
		st_Output(st_Buffer);
#endif

		ZFORTIFY_UNLOCK();
		return(0);       
	}

	// Initialize and validate the header
	h = (struct Header *)ptr;

	h->Size  = size;
  
	h->File  = file;
	h->Line  = line;  

	h->Next  = st_Head;
	h->Prev  = 0;
	h->Scope = st_Scope;

	if(st_Head)
	{
		st_Head->Prev = h;
		MakeHeaderValid(st_Head);  
	}

	st_Head = h;
  
	MakeHeaderValid(h);

	// Initialize the fortifications
	SetFortification(ptr + sizeof(struct Header), ZFORTIFY_BEFORE_VALUE, ZFORTIFY_BEFORE_SIZE);
	SetFortification(ptr + sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE + size,
					 ZFORTIFY_AFTER_VALUE, ZFORTIFY_AFTER_SIZE);

#ifdef FILL_ON_NEW 
	// Fill the actual user memory
	SetFortification(ptr + sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE, FILL_ON_NEW_VALUE, size);
#endif

	ZFORTIFY_UNLOCK();

	// We return the address of the user's memory, not the start of the block,
	// which points to our magic cookies
	return(ptr + sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE);
}

// operator delete() - This delete must be used for all memory allocated with
//                     the ZFortify new()
//
//   Features:
//     + Pointers are validated before attempting a delete - the pointer
//       must point to a valid new'd bit of memory.
//     + Detects attempts at deleting the same block of memory twice
//     + Can clear out memory as it is freed, to prevent code from using
//       the memory after it's been freed.
//     + Checks the sentinals of the memory being freed.
//     + Can check the sentinals of all memory.
int ZFORTIFY_STORAGE
ZFortify_PreDelete(char *file, unsigned long line)
{
	// Don't change the delete source code pointer if we already have some information,
	// cause if we're doing a delete inside a delete, the highest level delete will 
	// probably be the most useful.
	if(st_Delete_File == 0)
	{
		st_Delete_File = file;
		st_Delete_Line = line;   
	}	
	return 1;
}
 
void ZFORTIFY_STORAGE
operator delete(void *uptr)
{
	unsigned char *ptr = (unsigned char *)uptr - sizeof(struct Header) - ZFORTIFY_BEFORE_SIZE;
	struct Header *h = (struct Header *)ptr;
	char *file;
	unsigned long line;    

    // It is defined to be harmless to delete 0
	if(uptr == 0)
		return;

	ZFORTIFY_LOCK();

	if(st_Disabled)
	{
		free(uptr);
		ZFORTIFY_UNLOCK();
		return;
	}

	if(st_Delete_File)
	{
		file = st_Delete_File;
		line = st_Delete_Line;
	
		st_Delete_File = 0;
	}
	else
	{
		file = "delete";
		line = 0;
	}

#ifdef CHECK_ALL_MEMORY_ON_DELETE
	ZFortify_CheckAllMemory(file, line);
#endif  

#ifdef PARANOID_DELETE
	if(!IsOnList(h))
	{
		sprintf(st_Buffer,
				"\nZFortify: %s.%ld\n         Invalid pointer, corrupted header, or possible free twice\n",
				file, line);
		st_Output(st_Buffer);
		OutputLastVerifiedPoint();
		goto fail;
	}
#endif

	if(!CheckBlock(h, file, line))
		goto fail;
	
	// Remove the block from the list
	if(h->Prev)
	{
		if(!CheckBlock(h->Prev, file, line))
			goto fail;
	  
		h->Prev->Next = h->Next;
		MakeHeaderValid(h->Prev);
	}
	else
		st_Head = h->Next;

	if(h->Next)
	{
		if(!CheckBlock(h->Next, file, line))
			goto fail;

		h->Next->Prev = h->Prev;
		MakeHeaderValid(h->Next);    
	}    

#ifdef FILL_ON_DELETE
	// Nuke out all memory that is about to be freed
	SetFortification(ptr, FILL_ON_DELETE_VALUE, 
					 sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE + h->Size + ZFORTIFY_AFTER_SIZE);
#endif               

	// And do the actual free
	free(ptr);
	ZFORTIFY_UNLOCK();
	return;

fail:
	sprintf(st_Buffer, "         delete(%p) failed\n", uptr);
	st_Output(st_Buffer);  
	ZFORTIFY_UNLOCK();
}

// ZFortify_CheckPointer() - Returns true if the uptr points to a valid
// piece of ZFortify_new()'d memory. The memory must be on the new'd
// list, and it's sentinals must be in tact.
// If anything is wrong, an error message is issued.
//
//   (Note - if ZFortify is disabled, this function always returns true).
int ZFORTIFY_STORAGE
ZFortify_CheckPointer(void *uptr, char *file, unsigned long line)
{
	unsigned char *ptr = (unsigned char *)uptr - sizeof(struct Header) - ZFORTIFY_BEFORE_SIZE;
	int r;
  
	if(st_Disabled)
		return(1);

	ZFORTIFY_LOCK();

	if(!IsOnList((struct Header *)ptr))
	{
		sprintf(st_Buffer, "\nZFortify: %s.%ld\n         Invalid pointer or corrupted header detected (%p)\n",
							file, line, uptr);
		st_Output(st_Buffer);
		ZFORTIFY_UNLOCK();
		return(0);
	}

	r = CheckBlock((struct Header *)ptr, file, line);
	ZFORTIFY_UNLOCK();
	return r;
}

// ZFortify_SetOutputFunc(ZFortify_OutputFuncPtr Output) - Sets the function used to
// output all error and diagnostic messages by ZFortify. The output function 
// takes a single unsigned char * argument, and must be able to handle newlines.
//     The function returns the old pointer.
ZFortify_OutputFuncPtr ZFORTIFY_STORAGE
ZFortify_SetOutputFunc(ZFortify_OutputFuncPtr Output)
{
	ZFortify_OutputFuncPtr Old = st_Output;

	st_Output = Output;
  
	return(Old);
}

// ZFortify_SetNewFailRate(int Percent) - ZFortify_new() will make the
// new attempt fail this Percent of the time, even if the memory is
// available. Useful to "stress-test" an application. Returns the old
//  value. The fail rate defaults to 0.
int ZFORTIFY_STORAGE
ZFortify_SetNewFailRate(int Percent)
{
	int Old = st_NewFailRate;
  
	st_NewFailRate = Percent;
  
	return(Old);
}

 
// ZFortify_CheckAllMemory() - Checks the sentinals of all new'd memory.
// Returns the number of blocks that failed. 
//
//  (If ZFortify is disabled, this function always returns 0).
int ZFORTIFY_STORAGE
ZFortify_CheckAllMemory(char *file, unsigned long line)
{
	struct Header *curr = st_Head;
	int count = 0;

	if(st_Disabled)
		return(0);

	ZFORTIFY_LOCK();

	while(curr)
	{
		if(!CheckBlock(curr, file, line))
			count++;

		curr = curr->Next;      
	}
  
	if(count == 0)
	{
		st_LastVerifiedFile = file;
		st_LastVerifiedLine = line;
	}	

	ZFORTIFY_UNLOCK();
	return(count);
}

// ZFortify_EnterScope - enters a new ZFortify scope level. 
// returns the new scope level.
int ZFORTIFY_STORAGE
ZFortify_EnterScope(char */*file*/, unsigned long /*line*/)
{
	return(++st_Scope);
}

// ZFortify_LeaveScope - leaves a ZFortify scope level,
// also prints a memory dump of all non-freed memory that was allocated
// during the scope being exited.
int ZFORTIFY_STORAGE
ZFortify_LeaveScope(char *file, unsigned long line)
{
	struct Header *curr = st_Head;
	int count = 0;
	unsigned long size = 0;

	if(st_Disabled)
		return(0);

	ZFORTIFY_LOCK();

	st_Scope--;
	while(curr)
	{
		if(curr->Scope > st_Scope)
		{
			if(count == 0)
			{
				sprintf(st_Buffer, "\nZFortify: Memory Dump at %s.%ld\n", file, line);
				st_Output(st_Buffer);
				OutputLastVerifiedPoint();
				sprintf(st_Buffer, "%11s %8s %s\n", "Address", "Size", "Allocator");
				st_Output(st_Buffer);
			}	
								
			OutputHeader(curr);
			count++;
			size += curr->Size;
		}

		curr = curr->Next;      
	}

	if(count)
	{
		sprintf(st_Buffer, "%11s %8ld bytes overhead\n", "and",
						(unsigned long)(count * (sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE + ZFORTIFY_AFTER_SIZE)));
						st_Output(st_Buffer);

		sprintf(st_Buffer,"%11s %8ld bytes in %d blocks\n", "total", size, count);
		st_Output(st_Buffer);
	}

	ZFORTIFY_UNLOCK();
	return(count);
}

// ZFortify_OutputAllMemory() - Outputs the entire list of currently
// new'd memory. For each new'd block is output it's Address,
// Size, and the SourceFile and Line that allocated it.
//
// If there is no memory on the list, this function outputs nothing.
//
// It returns the number of blocks on the list, unless ZFortify has been
// disabled, in which case it always returns 0.
int ZFORTIFY_STORAGE
ZFortify_OutputAllMemory(char *file, unsigned long line)
{
	struct Header *curr = st_Head;
	int count = 0;
	unsigned long size = 0;

	if(st_Disabled)
		return(0);

	ZFORTIFY_LOCK();

	if(curr)
	{
		sprintf(st_Buffer, "\nZFortify: Memory Dump at %s.%ld\n", file, line);
		st_Output(st_Buffer);
		OutputLastVerifiedPoint();
		sprintf(st_Buffer, "%11s %8s %s\n", "Address", "Size", "Allocator");
		st_Output(st_Buffer);
								
		while(curr)
		{
			OutputHeader(curr);
			count++;
			size += curr->Size;
			curr = curr->Next;      
		}
					 
		sprintf(st_Buffer, "%11s %8ld bytes overhead\n", "and", 
			(unsigned long)(count * (sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE + ZFORTIFY_AFTER_SIZE)));
		st_Output(st_Buffer);

		sprintf(st_Buffer,"%11s %8ld bytes in %d blocks\n", "total", size, count);  
		st_Output(st_Buffer);
	}
  
	ZFORTIFY_UNLOCK();
	return(count);
}

// ZFortify_DumpAllMemory(Scope) - Outputs the entire list of currently
// new'd memory within the specified scope. For each new'd block is output
// it's Address, Size, the SourceFile and Line that allocated it, a hex dump
// of the contents of the memory and an ascii dump of printable characters.
//
// If there is no memory on the list, this function outputs nothing.
//
// It returns the number of blocks on the list, unless ZFortify has been
// disabled, in which case it always returns 0.
int ZFORTIFY_STORAGE
ZFortify_DumpAllMemory(int scope, char *file, unsigned long line)
{
	struct Header *curr = st_Head;
	int count = 0;
	unsigned long size = 0;

	if(st_Disabled)
		return(0);

	ZFORTIFY_LOCK();

	while(curr)
	{
		if(curr->Scope >= scope)
		{
			if(count == 0)
			{
				sprintf(st_Buffer, "\nZFortify: Memory Dump at %s.%ld\n", file, line);
				st_Output(st_Buffer);
				OutputLastVerifiedPoint();
				sprintf(st_Buffer, "%11s %8s %s\n", "Address", "Size", "Allocator");
				st_Output(st_Buffer);
			}

			OutputHeader(curr);
			OutputMemory(curr);
			st_Output("\n");
			count++;
			size += curr->Size;
		}

		curr = curr->Next;
	}

	if(count)
	{
		sprintf(st_Buffer, "%11s %8ld bytes overhead\n", "and",
						(unsigned long)(count * (sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE + ZFORTIFY_AFTER_SIZE)));
						st_Output(st_Buffer);

		sprintf(st_Buffer,"%11s %8ld bytes in %d blocks\n", "total", size, count);
		st_Output(st_Buffer);
	}

	ZFORTIFY_UNLOCK();
	return(count);
}

// ZFortify_Disable() - This function provides a mechanism to disable ZFortify
// without recompiling all the sourcecode. It can only be called, though,
// when there is no memory on the ZFortify new'd list. (Ideally, at the 
// start of the program before any memory has been allocated). If you
// call this function when there IS memory on the ZFortify new'd list,
// it will issue an error, and ZFortify will not be disabled.
int ZFORTIFY_STORAGE
ZFortify_Disable(char *file, unsigned long line)
{
	int result;
	ZFORTIFY_LOCK();

	if(st_Head)
	{
		sprintf(st_Buffer, "ZFortify: %s.%d\n", file, line);
		st_Output(st_Buffer);
		st_Output("         ZFortify_Disable failed\n");
		st_Output("         (because there is memory on the ZFortify memory list)\n");
	
		ZFortify_OutputAllMemory(file, line);
		result = 0;            
	}
	else
	{
		st_Disabled = 1;
		result = 1;
	}
  
	ZFORTIFY_UNLOCK();
	return(result);
}

// Check a block's header and fortifications.
static int
CheckBlock(struct Header *h, char *file, unsigned long line)
{   
	unsigned char *ptr = (unsigned char *)h;
	int result = 1;

	if(!IsHeaderValid(h))
	{
		sprintf(st_Buffer, "\nZFortify: %s.%ld\n         Invalid pointer or corrupted header detected (%p)\n",
							file, line, ptr + sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE);
		st_Output(st_Buffer);
		OutputLastVerifiedPoint();
		return(0);
	}

	if(!CheckFortification(ptr + sizeof(struct Header), ZFORTIFY_BEFORE_VALUE, ZFORTIFY_BEFORE_SIZE))
	{
		sprintf(st_Buffer, "\nZFortify: %s.%ld\n         Memory overrun detected before block\n",
							file, line);
		st_Output(st_Buffer);

		sprintf(st_Buffer,"         (%p,%ld,%s.%ld)\n", ptr + sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE,
						  (unsigned long)h->Size, h->File, h->Line);
		st_Output(st_Buffer);
	
		OutputFortification(ptr + sizeof(struct Header), ZFORTIFY_BEFORE_VALUE, ZFORTIFY_BEFORE_SIZE);
		OutputLastVerifiedPoint();
		result = 0;
	}

	if(!CheckFortification(ptr + sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE + h->Size,
							ZFORTIFY_AFTER_VALUE, ZFORTIFY_AFTER_SIZE))
	{
		sprintf(st_Buffer, "\nZFortify: %s.%ld\n         Memory overrun detected after block\n",
							file, line);
		st_Output(st_Buffer);

		sprintf(st_Buffer,"         (%p,%ld,%s.%ld)\n",
						ptr + sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE,
						(unsigned long)h->Size, h->File, h->Line);
		st_Output(st_Buffer);

		OutputFortification(ptr + sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE + h->Size,
							ZFORTIFY_AFTER_VALUE, ZFORTIFY_AFTER_SIZE);
		OutputLastVerifiedPoint();
		result = 0;
	}
 
	return(result);    
}

// Checks if the _size_ bytes from _ptr_ are all set to _value_
static int 
CheckFortification(unsigned char *ptr, unsigned char value, size_t size)
{
	while(size--)
		if(*ptr++ != value)
			return(0);

	return(1);      
}

// Set the _size_ bytes from _ptr_ to _value_.
static void 
SetFortification(unsigned char *ptr, unsigned char value, size_t size)
{
	memset(ptr, value, size);
}

// Output the corrupted section of the fortification
static void
OutputFortification(unsigned char *ptr, unsigned char /*value*/, size_t size)
{
	unsigned long offset, column;
	char	ascii[17];

	st_Output("Address     Offset Data");

	offset = 0;
	column = 0;

	while(offset < size)
	{
		if(column == 0)
		{
			sprintf(st_Buffer, "\n%8p %8d ", ptr, offset);
			st_Output(st_Buffer);
		}

		sprintf(st_Buffer, "%02x ", *ptr);
		st_Output(st_Buffer);

		ascii[ column ] = isprint( *ptr ) ? char(*ptr) : char(' ');
		ascii[ column + 1 ] = '\0';

		ptr++;
		offset++;
		column++;

		if(column == 16)
		{
			st_Output( "   \"" );
			st_Output( ascii );
			st_Output( "\"" );
			column = 0;
		}
	}

	if ( column != 0 )
	{
		while ( column ++ < 16 )
		{
			st_Output( "   " );
		}
		st_Output( "   \"" );
		st_Output( ascii );
		st_Output( "\"" );
	}

	st_Output("\n");
}

// Returns true if the supplied pointer does indeed point to a real Header
static int 
IsHeaderValid(struct Header *h)                                
{
	return(!ChecksumHeader(h));
}

// Updates the checksum to make the header valid
static void 
MakeHeaderValid(struct Header *h)
{
	h->Checksum = 0;
	h->Checksum = -ChecksumHeader(h);
}

// Calculate (and return) the checksum of the header. (Including the Checksum
// variable itself. If all is well, the checksum returned by this function should
// be 0.
static int 
ChecksumHeader(struct Header *h)
{
	int c, checksum, *p;
  
	for(c = 0, checksum = 0, p = (int *)h; c < sizeof(struct Header)/sizeof(int); c++)
		checksum += *p++;  
	
	return(checksum);
}                  

// Examines the new'd list to see if the given header is on it.
static int 
IsOnList(struct Header *h)
{
	struct Header *curr;
  
	curr = st_Head;
	while(curr)
	{
		if(curr == h)
			return(1);
	  
		curr = curr->Next;
	}

	return(0);
}

// Output the header...
static void
OutputHeader(struct Header *h)
{
	sprintf(st_Buffer, "%11p %8ld %s.%ld (%d)\n",
						(unsigned char*)h + sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE,
						(unsigned long)h->Size,
						h->File, h->Line, h->Scope);
	st_Output(st_Buffer);
}

// Hex and ascii dump the memory
static void
OutputMemory(struct Header *h)
{
	OutputFortification((unsigned char*)h + sizeof(struct Header) + ZFORTIFY_BEFORE_SIZE,
						0, h->Size);
}

static void 
OutputLastVerifiedPoint()
{
	sprintf(st_Buffer, "\nLast Verified point: %s.%ld\n", 
					  st_LastVerifiedFile,
					  st_LastVerifiedLine);
	st_Output(st_Buffer);
}

#ifdef AUTOMATIC_LOG_FILE
// Automatic log file stuff!

// AutoLogFile class. There can only ever be ONE of these 
// instantiated! It is a static class, which means that
// it's constructor will be called at program initialization,
// and it's destructor will be called at program termination.
// We don't know if the other static class objects have been
// constructed/destructed yet, but this pretty much the best
// we can do with standard C++ language features.
class AutoLogFile
{
	static FILE *fp;
	static int   written_something;

public:
	AutoLogFile()
	{
		written_something = 0;
		ZFortify_SetOutputFunc(AutoLogFile::Output);
		ZFortify_EnterScope("Program Initialization",0);
	}

	static void Output(const char *s)
	{
		if(written_something == 0)
		{
			FIRST_ERROR_FUNCTION;
			fp = fopen(LOG_FILENAME, "w");
			if(fp)
			{
				time_t t;
				time(&t);
				fprintf(fp, "ZFortify log started at %s\n", ctime(&t));
				written_something = 1;
			}	
		}

		if(fp)
		{
			fputs(s, fp);
			fflush(fp);
		}	
	}
	
	~AutoLogFile()
	{
		ZFortify_LeaveScope("Program Termination",0);
		if(fp)
		{
			time_t t;
			time(&t);
			fprintf(fp, "\nZFortify log closed at %s\n", ctime(&t));
			fclose(fp);
			fp = 0;
		}	
	}
};

FILE *AutoLogFile::fp = 0;
int   AutoLogFile::written_something = 0;

static AutoLogFile Abracadabra;

#endif // AUTOMATIC_LOG_FILE
#endif // ZFORTIFY

