/*
 * (c) Copyright 1990 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 <fcntl.h>
#include "malloc.h"

/*
 * Function:	malloc_chain_check()
 *
 * Purpose:	to verify malloc chain is intact
 *
 * Arguments:	todo	- 0 - just check and return status
 *			  1 - call malloc_warn if error detected
 *
 * Returns:	0	- malloc chain intact & no overflows
 *		other	- problems detected in malloc chain
 *
 * Narrative:
 *
 * Notes:	If todo is non-zero the malloc_warn function, when called
 *		may not return (i.e. it may exit)
 *
 */
#ifndef lint
static
char rcs_hdr[] = "$Id: mlc_chn.c,v 1.1.1.1 1997/07/15 16:02:26 dvadura Exp $";
#endif


int
malloc_chain_check(todo)
	int		  todo;
{
	char			* func = "malloc_chain_check";
	int			  i;
	extern char		* malloc_data_start;
	extern char		* malloc_data_end;
	extern struct mlist 	* malloc_end;
	extern int		  malloc_errno;
	extern struct mlist	  malloc_start;
	struct mlist		* oldptr;
	struct mlist		* ptr;
	int			  rtn = 0;

	oldptr = &malloc_start;
	for(ptr = malloc_start.next; ; ptr = ptr->next)
	{
		/*
		 * Since the malloc chain is a forward only chain, any
		 * pointer that we get should always be positioned in 
		 * memory following the previous pointer.  If this is not
		 * so, we must have a corrupted chain.
		 */
		if( ptr )
		{
			if(ptr < oldptr )
			{
				malloc_errno = M_CODE_CHAIN_BROKE;
				if( todo )
				{
					malloc_fatal(func);
				}
				rtn++;
				break;
			}
			oldptr = ptr;
		}
		else
		{
			if( oldptr != malloc_end )
			{
				/*
				 * This should never happen.  If it does, then
				 * we got a real problem.
				 */
				malloc_errno = M_CODE_NO_END;
				if( todo )
				{
					malloc_fatal(func);
				}
				rtn++;
			}
			break;
		}
		
		/*
		 * verify that ptr is within the malloc region...
		 * since we started within the malloc chain this should never
		 * happen.
		 */

		if( ((char *)ptr < malloc_data_start) ||
		    ((char *)ptr > malloc_data_end) )
		{
			malloc_errno = M_CODE_BAD_PTR;
			if( todo )
			{
				malloc_fatal(func);
			}
			rtn++;
			break;
		}

		/* 
		 * verify magic flag is set
		 */
		
		if( (ptr->flag&M_MAGIC) != M_MAGIC )
		{
			malloc_errno = M_CODE_BAD_MAGIC;
			if( todo )
			{
				malloc_warning(func);
			}
			rtn++;
			continue;
		}

		/* 
		 * verify segments are correctly linked together
		 */
		
		if( (ptr->prev && (ptr->prev->next != ptr) ) ||
		    (ptr->next && (ptr->next->prev != ptr) ) ||
		    ((ptr->next == NULL) && (ptr->prev == NULL)) )
		{
			malloc_errno = M_CODE_BAD_CONNECT;
			if( todo )
			{
				malloc_warning(func);
			}
			rtn++;
			continue;
		}

		/*
		 * If this segment is allocated
		 */

		if( (ptr->flag & M_INUSE) != 0 )
		{
			/*
			 * verify no overflow of data area
			 */

			for(i=ptr->r_size; i < ptr->s.size; i++)
			{
				if( ptr->data[i] != M_FILL )
				{
					malloc_errno = M_CODE_OVERRUN;
					if( todo )
					{
						malloc_warning(func);
					}
					rtn++;
					break;
				}
			}
		}
		else /* it's not allocated so */
		{
			/*
			 * verify no reuse of freed data blocks
			 */

			for(i=0; i < ptr->s.size; i++)
			{
				if( ptr->data[i] != M_FREE_FILL )
				{
					malloc_errno = M_CODE_REUSE;
					if( todo )
					{
						malloc_warning(func);
					}
					rtn++;
					break;
				}
			}
		}

	} /* for(... */

	return(rtn);

} /* malloc_chain_check(... */
