/*
 * (c) Copyright 1992 Conor P. Cahill (uunet!virtech!cpcahil).  
 * You may copy, distribute, and use this software as long as this
 * copyright statement is not removed.
 */
#include <stdio.h>
#include "mallocin.h"

static long 	fillmalloc = -1;
static long 	fillfree   = -1;

#define FILLINIT() if( fillmalloc == -1 )  FillInit()


/*
 * Fill check types
 */
#define FILL_UNDER 	1
#define FILL_OVER	2
#define FILL_FREED	3

/*
 * FillInit() - fill initialization routine
 */
VOIDTYPE
FillInit()
{
	char	* ptr;
	int	  i;
	
	ptr = (char *) &fillmalloc;

	for( i=0; i < sizeof(long); i++)
	{
		*ptr++ = M_FILL;
	}

	ptr = (char *) &fillfree;

	for( i=0; i < sizeof(long); i++)
	{
		*ptr++ = M_FREE_FILL;
	}

} /* FillInit(... */

/*
 * FillCheck()	check for overflow and/or underflow using the filled regions
 *		in the specified malloc segment.
 */
int
FillCheck(func,file,line,ptr,showerrors)
	CONST char	* func;
	CONST char	* file;
	int		  line;
	struct mlist	* ptr;
	int		  showerrors;
{
	int		  rtn = -1;

	FILLINIT();

	/*
	 * if this block has no filling
	 */
	if( (ptr->flag & (M_FILLED|M_DFILLED)) == 0 )
	{
		rtn = 0;
	}
	/*
	 * else if this block is in use or it was not free-filled
	 */
	else if(    ((ptr->flag & M_INUSE)  != 0) 
		 || ((ptr->flag & M_FILLED) == 0) )
	{
		/*
		 * check for data underflow and data overflow
		 */
		rtn =   FillCheckData(func,file,line,ptr,FILL_UNDER,showerrors)
		      + FillCheckData(func,file,line,ptr,FILL_OVER, showerrors);
	}
	/*
	 * else the block must have been freed
	 */
	else
	{
		/*
		 * check for data underflow and free filling
		 */
		rtn =   FillCheckData(func,file,line,ptr,FILL_UNDER,showerrors)
		      + FillCheckData(func,file,line,ptr,FILL_FREED,showerrors);
	}

	return(rtn);

} /* FillCheck(... */

/*
 * FillCheckData() - routine to check the data areas to ensure that they
 * 		 are filled with the appropriate fill characters.
 */
int
FillCheckData(func,file,line,ptr,checktype,showerrors)
	CONST char	* func;
	CONST char	* file;
	int		  line;
	struct mlist	* ptr;
	int		  checktype;
	int		  showerrors;
{
	register char		* data = NULL;
	int			  errcode = 0;
	char			  filler = '\0';
	long			  fillword = 0;
	register SIZETYPE	  i = 0;
	register long		* ldata;
	SIZETYPE		  limit = ptr->s.size;
	int			  rtn = -1;

	if( (ptr->flag & (M_DFILLED | M_FILLED)) == 0 )
	{
		return(0);
	}

	switch(checktype)
	{
		case FILL_UNDER:
			if(    ((ptr->flag & M_DFILLED) == 0 )
			    || (ptr->s.filler[LONGFILL-1] == fillmalloc) )
			{
				rtn = 0;
			}
			else
			{

				/*
				 * if showing errors
			 	 */
				if( showerrors )
				{
					/*
					 * give the underrun error message
					 */
					malloc_errno = M_CODE_UNDERRUN;
					malloc_warning(func,file,line,ptr);
					/*
					 * fix the problem
					 */
					ptr->s.filler[LONGFILL-1] = fillmalloc;
				}
				rtn = 1;
			}
			break;

		case FILL_OVER:
			if( (ptr->flag & M_DFILLED) == 0)
			{
				rtn = 0;
			}
			else
			{
				i        = ptr->r_size;
				errcode  = M_CODE_OVERRUN;
				filler   = M_FILL;
				fillword = fillmalloc;
				data	 = ptr->data;
			}
			break;

		case FILL_FREED:
			if( (ptr->flag & M_FILLED) == 0)
			{
				rtn = 0;
			}
			else
			{
				i        = 0;
				errcode  = M_CODE_REUSE;
				filler   = M_FREE_FILL;
				fillword = fillfree;
				data	 = ptr->data;
			}
			break;

		default:
			i = 0; 
			limit = 0;
			break;
	}

	/*
	 * if there is nothing to check, just return 
	 */
	if( rtn != -1 )
	{
		return(rtn);
	}
			
	rtn = 0;
	data += i;
	i = limit - i;
	while( ((long)data) & (sizeof(long)-1) )
	{
		if( *data != filler )
		{
			if( showerrors )
			{
				malloc_errno = errcode;
				malloc_warning(func,file,line,ptr);

				/*
				 * fix the underrun so we only get this
				 * message once
				 */
				while( i-- > 0 )
				{
					*(data++) = filler;
				}
			}

			rtn++;
					
			break;
		}
		data++;
		i--;
	}

	/*
	 * if we haven't found an error yet
	 */
	if( rtn == 0 )
	{
		/*
		 * convert to use longword pointer
		 */
		ldata = (long *) data;
		i >>= 2;

		/*
		 * while more longwords to check
		 */	
		while( i > 0 )
		{
			if( *ldata != fillword )
			{
				if( showerrors )
				{
					malloc_errno = errcode;
					malloc_warning(func,file,line,ptr);
	
					/*
					 * fix the underrun so we only get this
					 * message once
					 */
					while( i-- > 0 )
					{
						*(ldata++) = fillword;
					}
				}
	
				rtn++;
				break;
			}

			ldata++;
			i--;
		
		}
		
	}

	return(rtn);

} /* FillCheckData(... */

/*
 * FillData()	fill in the needed areas in the specified segment.  This is used
 *		to place non-zero data in new malloc segments, setup boundary
 *		areas to catch under/overflow, and overwrite freed segments so 
 *		that they can't be usefully re-used.
 */
void
FillData(ptr,alloctype,start,nextptr)
	register struct mlist	* ptr;
	int			  alloctype;
	SIZETYPE		  start;
	struct mlist		* nextptr;
{
	int		  fills;
	int		  filler = 0;
	SIZETYPE	  limit = ptr->s.size;
	
	FILLINIT();

	if( (malloc_opts & (MOPT_MFILL|MOPT_DFILL|MOPT_FFILL)) == 0)
	{
		ptr->flag &= ~(M_FILLED|M_DFILLED);
		return;
	}

	switch(alloctype)
	{
		case FILL_MALLOC:
			fills = MOPT_MFILL | MOPT_DFILL;
			filler = M_FILL;
			break;

		case FILL_FREE:
			fills = MOPT_FFILL;
			filler = M_FREE_FILL;
			break;

		case FILL_SPLIT:
			fills = MOPT_FFILL;
			filler = M_FREE_FILL;
			break;

		case FILL_JOIN:
			if( (ptr->flag&M_INUSE) != 0 )
			{
				if( ptr->flag & M_FILLED )
				{
					fills = MOPT_MFILL;
					filler = M_FILL;
				}
				else if(  ptr->flag & M_DFILLED )
				{
					fills = MOPT_MFILL;
					filler = M_FILL;
					start = ptr->r_size;;
				}
				else
				{
					fills = 0;
				}
			}
			else /* not in use */
			{
				if( ptr->flag & M_FILLED )
				{
					fills = MOPT_MFILL;
					filler = M_FREE_FILL;

					/*
					 * if the next segment is already filled
					 */
					if( (nextptr->flag & M_FILLED) != 0 )
					{
						limit = start + M_SIZE;
					}
				}
				else if( ((ptr->flag & M_DFILLED) != 0)
					&& ((nextptr->flag & M_DFILLED) == 0) )
				{
					fills = MOPT_MFILL;
					filler = M_FILL;
					start = ptr->r_size;;
				}
				else
				{
					fills = 0;
				}
			}
			break;

		case FILL_REALLOC:
			if( ptr->flag & M_FILLED )
			{
				fills = MOPT_MFILL;
				filler = M_FILL;
			}
			else if( ptr->flag & M_DFILLED )
			{
				fills = MOPT_MFILL;
				filler = M_FILL;
				start = ptr->r_size;;
			}
			else
			{
				fills = 0;
			}
			break;

		case FILL_CALLOC:
			fills = MOPT_DFILL;
			break;

		default:
			fills = 0;
	}


	if( (fills & MOPT_DFILL) != 0 )
	{
		if( (malloc_opts & MOPT_DFILL) != 0 )
		{
			ptr->s.filler[LONGFILL-1] = fillmalloc;
			ptr->flag |= M_DFILLED;
		}
		else if( alloctype != FILL_FREE ) 
		{
			ptr->flag &= ~M_DFILLED;
		}
	}

	if( (malloc_opts & (MOPT_MFILL|MOPT_FFILL) & fills) != 0 )
	{
		/*
		 * fill in the data
		 */
		DataMS(ptr->data+start, filler, (MEMSIZE) (limit - start));

		ptr->flag |= M_FILLED;
	}
	else 
	{
		ptr->flag &= ~M_FILLED;

		/*
		 * if we still have to fill the boundry area and this isn't 
		 * a free-fill, go do it
		 */
		if( ((ptr->flag & M_DFILLED) != 0) && (filler == M_FILL) )
		{
			DataMS(ptr->data+ptr->r_size, M_FILL, 
					(MEMSIZE) (limit - ptr->r_size));
		}

	}

} /* FillData(... */

