/*    This file heap_chk.c, should be compiled and added to bcl286.lib
    with tlib.  Or, if you are less brave, just link it in to files
    that need it explicitly.*/

#include <stdio.h>
#include <stdlib.h>
#include <alloc.h>

#include <dos.h>
#include <phapi.h>

#ifdef __BORLANDC__

/*
** these structures below are based on information in segheap.asm
** as modified by Phar Lap Software.  This data is not expected to
** change in the future, but may.
*/

struct pharheapinfo
{
	unsigned size;
	unsigned prev_real;
	unsigned prev_free;
	unsigned next_free;
};

extern unsigned __base;
extern unsigned __top;
extern unsigned __hincr;
	
int heapwalk(struct heapinfo *hi)
{	
	int rc;
	struct pharheapinfo *this,*next;
	
	if (hi->ptr == NULL)
	{
		/* if there is no heap, return _HEAPEMPTY */
		if (__base == 0) {
			rc = _HEAPEMPTY;
			goto end_walk;
		}

		/* find the start of the heap */
		this = MAKEP(__base,0);
		next = MAKEP(__base,this->prev_real);
	}
	else
	{
		/* BELOW NOT NECESSARY, ASSUMES HEAP IS CORRECT */
		/* Borland C++ Library Reference for heapwalk() */
		/* check out the pointer, first..... */
/*		if (heapchecknode((void *)hi)==_BADNODE) {
**			rc = _HEAPCORRUPT;
**			got end_walk;
**		}
*/
		
		/* get the start of this pharheapinfo structure */
		this = MAKEP(FP_SEG(hi->ptr),FP_OFF(hi->ptr));
		
		/* if this is the end, return _HEAPEND */
		if ((FP_SEG(hi->ptr) == __top) &&
			(FP_OFF(hi->ptr) == 0)) {
			rc = _HEAPEND;
			goto end_walk;
			}

		/* find the next block in the heap */
		if (FP_OFF(hi->ptr) == 0)
			this = MAKEP(FP_SEG(hi->ptr)+__hincr,0);
		next = MAKEP(FP_SEG(this),this->prev_real);
	}

	/* fill in the values for the heapinfo structure */
	hi->in_use = next->size & 1L;
	hi->size = next->size & 0xFFFE;
	hi->ptr = next;
	rc = _HEAPOK;

end_walk:
	return(rc);
}


int heapfillfree(unsigned int fillvalue)
{
	struct heapinfo hi;
	char *area;
	int rc;
	int i;

	hi.ptr = NULL;
	
	if ((rc=heapwalk(&hi))!=_HEAPEMPTY) {
		do {
			if (hi.in_use == 0) {
				area = (char *)hi.ptr;
				if (hi.size<8) {
					rc = _HEAPCORRUPT;
					goto exit_fill;
				}
				for (i=0;i<(hi.size-8);i++) {
					area[i+8] = fillvalue;
				}
			}				
		}
		while ((rc=heapwalk(&hi))!=_HEAPEND);
		if (rc == _HEAPEND)
			rc = _HEAPOK;
		else
			rc = _HEAPCORRUPT;
	}

exit_fill:	
	
	return(rc);
}

int heapcheckfree(unsigned int fillvalue)
{
	struct heapinfo hi;
	char *area;
	int rc;
	int i;

	hi.ptr = NULL;
	
	if ((rc=heapwalk(&hi))!=_HEAPEMPTY) {
		do {
			if (hi.in_use == 0) {
				area = (char *)hi.ptr;
				for (i=0;i<(hi.size-8);i++) {
					if (hi.size<8) {
						rc = _HEAPCORRUPT;
						goto exit_check;
					}
					if (area[i+8]!=fillvalue) {
						rc = _BADVALUE;
						goto exit_check;
					}
				}
			}
		}
		while ((rc=heapwalk(&hi))!=_HEAPEND);
		if (rc == _HEAPEND)
			rc = _HEAPOK;
		else
			rc = _HEAPCORRUPT;
	}

exit_check:
	
	return(rc);
}

/* 
** this is an experiment in controlled paranoia
** the pointer passed in may or may not be valid, so lets see if
** we can keep the program from dying in that case.....
*/
int pharchecknode(void *node)
{
	int rc;
	unsigned seg;
	unsigned off;
	unsigned end;
	struct pharheapinfo *plnode;
	
	seg = FP_SEG(node);
	off = FP_OFF(node);
	
	/* check the segment, verify that it is within the LDT and heap */
	/* the offset must be even */
	if ((seg&7)!=7 || seg<__base || seg>__top || (off&1)!=0) {
		rc = _BADNODE;
		goto exit_plnode;
	}

	/* all segments in the heap have 64K limits, so the pointer is safe */
	plnode = (struct pharheapinfo *)node;

	/* now, check to see if this is a valid node */
	/*** is the size+offset less than the limit ***/
	end = off+(plnode->size&0xFFFE);
	if (((end < off) && (end != 0)) ||
		((plnode->size < 8) && (off != 0)) ||
		((plnode->size < 8) && (plnode->size > 0) && (off == 0))) {
		rc = _BADNODE;
		goto exit_plnode;
	}

	if (plnode->size&0xFFFE != 0)
		rc = _USEDENTRY;
	else
		rc = _FREEENTRY;
	
exit_plnode:		
	return(rc);
}

int heapchecknode(void *node)
{
	int rc;
	int free;
	struct pharheapinfo *plnode;
	void *check;

	rc = pharchecknode(node);
	if (rc == _BADNODE) goto exit_checknode;
	free = rc;
	
	/*** does the prev_real point to another good block? ***/
	plnode = (struct pharheapinfo *)node;
	check = MAKEP(FP_SEG(node),plnode->prev_real);
	rc = pharchecknode(check);
	if (rc == _BADNODE) goto exit_checknode;
	
	/*** if the block is free, are next_free and prev_free valid? ***/
	if (free == _FREEENTRY)
	{
		check = MAKEP(FP_SEG(node),plnode->next_free);
		rc = pharchecknode(check);
		if (rc != _FREEENTRY) {
			rc = _HEAPCORRUPT;
			goto exit_checknode;
		}
		
		check = MAKEP(FP_SEG(node),plnode->prev_free);
		rc = pharchecknode(check);
		if (rc != _FREEENTRY) {
			rc = _HEAPCORRUPT;
			goto exit_checknode;
		}
	}

	rc = free;
	
exit_checknode:
	return(rc);
}

int heapcheck(void)
{
	int rc;
	struct heapinfo this;

	this.ptr = NULL;
	/* all heapwalk does is get the supposed starting point */
	rc = heapwalk(&this);
	if (rc == _HEAPEMPTY) goto exit_check;

	do {
		/* check the current pointer */
		rc = heapchecknode((void *)(this.ptr));
		if (rc == _BADNODE) {
			rc = _HEAPCORRUPT;
			goto exit_check;
		}
		/* if it isn't bad, we can continue to walk */
		rc = heapwalk(&this);
	} while (rc != _HEAPEND);

	rc = _HEAPOK;
	
exit_check:
	return(rc);
}


#endif
