/*
 * (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 "malloc.h"
#include "debug.h"

/*
 * Function:	free()
 *
 * Purpose:	to deallocate malloced data
 *
 * Arguments:	ptr	- pointer to data area to deallocate
 *
 * Returns:	nothing of any value
 *
 * Narrative:
 *		verify pointer is within malloc region
 *		get mlist pointer from passed address
 *		verify magic number
 *		verify inuse flag
 *		verify pointer connections with surrounding segments
 *		turn off inuse flag
 *		verify no data overrun into non-malloced area at end of segment
 *		IF possible join segment with next segment
 *		IF possible join segment with previous segment
 *		Clear all data in segment (to make sure it isn't reused)
 *
 */
#ifndef lint
static
char rcs_hdr[] = "$Id: free.c,v 1.1 1992/01/24 03:29:02 dvadura Exp $";
#endif

void
free(cptr)
	char	* cptr;
{
	char			* func = "free";
	int			  i;
	extern int		  malloc_checking;
	extern struct mlist	* malloc_end;
	extern int		  malloc_errno;
	extern char		* malloc_data_end;
	extern char		* malloc_data_start;
	void			  malloc_join();
	void			  malloc_memset();
	struct mlist		* oldptr;
	struct mlist		* ptr;

	/*
	 * IF malloc chain checking is on, go do it.
	 */
	if( malloc_checking )
	{
		(void) malloc_chain_check(1);
	}

	/*
	 * verify that cptr is within the malloc region...
	 */
	if( cptr < malloc_data_start || cptr > malloc_data_end )
	{
		malloc_errno = M_CODE_BAD_PTR;
		malloc_warning(func);
		return;
	}

	/* 
	 * convert pointer to mlist struct pointer.  To do this we must 
	 * move the pointer backwards the correct number of bytes...
	 */
	
	ptr = (struct mlist *) (cptr - M_SIZE);
	
	if( (ptr->flag&M_MAGIC) != M_MAGIC )
	{
		malloc_errno = M_CODE_BAD_MAGIC;
		malloc_warning(func);
		return;
	}

	if( ! (ptr->flag & M_INUSE) )
	{
		malloc_errno = M_CODE_NOT_INUSE;
		malloc_warning(func);
		return;
	}

 	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;
		malloc_warning(func);
		return;
	}

	ptr->flag &= ~M_INUSE;

	/*
	 * verify that the user did not overrun the requested number of bytes.
	 */
	for(i=ptr->r_size; i < ptr->s.size; i++)
	{
		if( ptr->data[i] != M_FILL )
		{
			malloc_errno = M_CODE_OVERRUN;
			malloc_warning(func);
			break;
		}
	}

	DEBUG3(10,"pointers: prev: 0x%.7x,  ptr: 0x%.7x, next: 0x%.7x",
			ptr->prev, ptr, ptr->next);
	
	DEBUG3(10,"size:     prev: %9d,  ptr: %9d, next: %9d",
			ptr->prev->s.size, ptr->s.size, ptr->next->s.size);
	
	DEBUG3(10,"flags:    prev: 0x%.7x,  ptr: 0x%.7x, next: 0x%.7x",
			ptr->prev->flag, ptr->flag, ptr->next->flag);
	
	/*
	 * check to see if this block can be combined with the next and/or
	 * previous block.  Since it may be joined with the previous block
	 * we will save a pointer to the previous block and test to verify
	 * if it is joined (it's next ptr will no longer point to ptr).
 	 */
	malloc_join(ptr,ptr->next,0,0);
	
	oldptr = ptr->prev;

	malloc_join(ptr->prev, ptr,0,0);

	if( oldptr->next != ptr )
	{
		DEBUG0(10,"Oldptr was changed");
		ptr = oldptr;
	}
	
	/*
	 * fill this block with '\02's to ensure that nobody is using a 
	 * pointer to already freed data...
	 */
	malloc_memset(ptr->data,M_FREE_FILL,(int)ptr->s.size);

}

/*
 * $Log: free.c,v $
 * Revision 1.1  1992/01/24  03:29:02  dvadura
 * dmake Version 3.8, Initial revision
 *
 * Revision 1.9  90/08/29  21:22:48  cpcahil
 * miscellaneous lint fixes
 * 
 * Revision 1.8  90/05/11  00:13:08  cpcahil
 * added copyright statment
 * 
 * Revision 1.7  90/02/25  11:00:18  cpcahil
 * added support for malloc chain checking.
 * 
 * Revision 1.6  90/02/24  21:50:18  cpcahil
 * lots of lint fixes
 * 
 * Revision 1.5  90/02/24  17:29:13  cpcahil
 * changed $Header to $Id so full path wouldnt be included as part of rcs 
 * id string
 * 
 * Revision 1.4  90/02/24  15:15:32  cpcahil
 * 1. changed ALREADY_FREE errno to NOT_INUSE so that the same errno could
 *    be used by both free and realloc (since it was the same error).
 * 2. fixed coding bug
 * 
 * Revision 1.3  90/02/24  14:23:45  cpcahil
 * fixed malloc_warning calls
 * 
 * Revision 1.2  90/02/24  13:59:10  cpcahil
 * added function header.
 * Modified calls to malloc_warning/malloc_fatal to use new code error messages
 * Added support for malloc_errno setting of error codes.
 * 
 * Revision 1.1  90/02/22  23:17:43  cpcahil
 * Initial revision
 * 
 */
