[alt.sources] Debugging library for programs that use malloc

cpcahil@virtech.uucp (Conor P. Cahill) (01/30/90)

The following is a "beta" release of a debugging library I have put 
together.  The purpose of the library is to catch many of the errors
made when using malloc'd memory.  See the README for more information.

I have compiled the code on System V, Ultrix, and HP-UX.  Check the
Makefile for any system dependent changes that need to be made.

If you use this on your system, I would like some feedback as to 
it's usefullness, suggested additions, fixes, complaints, etc.

After a short beta period I will be submitting the package to
comp.sources.unix, so I would appreciate feedback as soon as you can
get it to me.

Good luck and enjoy.

Conor P. Cahill

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	Makefile
#	README
#	calloc.c
#	debug.h
#	design
#	dump.c
#	free.c
#	func.hdr
#	malloc.c
#	malloc.h
#	malloc_chk.c
#	memory.c
#	pgm.c
#	realloc.c
#	string.c
#	testmalloc.c
#	testmem.c
#	toascii.c
#	toascii.h
# This archive created: Tue Jan 30 04:28:37 1990
cat << \SHAR_EOF > Makefile
CC=cc
# for System V systems use this CFLAGS
CFLAGS=-g -DSYS5
# else for BSD use:
CFLAGS=-g

LIB=libmalloc.a

OBJS=	malloc.o	\
	free.o		\
	realloc.o	\
	calloc.o	\
	string.o	\
	malloc_chk.o	\
	memory.o	\
	toascii.o	\
	dump.o

TESTS=testmalloc testmem

all:	$(LIB) pgm $(TESTS)

clean:  
	rm -f $(TESTS) pgm $(LIB) *.o
	
pgm:	$(LIB) pgm.o
	$(CC) -o $@ pgm.o $(LIB)

$(LIB): $(OBJS)
	ar ru $(LIB) $(OBJS)
	-if test -s /bin/ranlib; then /bin/ranlib $(LIB); else exit 0; fi
	-if test -s /usr/bin/ranlib; then /usr/bin/ranlib $(LIB); else exit 0; fi

testmalloc:	$(LIB) testmalloc.o
	$(CC) -o $@ testmalloc.o $(LIB)

testmem:	$(LIB) testmem.o
	$(CC) -o $@ testmem.o $(LIB)

$(OBJS):	malloc.h

toascii.o malloc.o dump.o:	toascii.h
SHAR_EOF
cat << \SHAR_EOF > README

This package is a collection of routines which are a drop-in replacement
for the malloc(3), memory(3), string(3), and bstring(3) library functions.

The purpose of these programs is to aid the development and/or debugging
of programs using these functions by providing a high level of consistancy
checking whenever a malloc pointer is used.  Due to this increased 
level of consistancy checking, these functions have a considerably larger
overhead than the standard functions, but the extra checking should be
well worth it in a development environment.

To use these functions all you need to do is compile the library and
include it on your loader command line.  You do not need to recompile
your code, only a relink is necessary.  

Features of this library:

 1. The malloced area returned from each call to malloc is filled with
    non-null bytes.  This should catch any use of uninitialized malloc
    area.  The fill pattern for malloced area is 0x01.

 2. When free is called numerous validity checks are made on the 
    pointer it is passed.  In addition, the data in the malloc block
    beyound the size requested on the initial malloc is checked to 
    verify that it is still filled with the original fill characters.

	This is usefull for catching things like:

		ptr = malloc(5);
		ptr[5] = '\0';

		/*
		 * You should not that this will be caught when it is
		 * freed not when it is done
		 */

    And finally, the freed block is filled with a different fill pattern
    so that you can easily determine if you are still using free'd space.
    The fill pattern for free'd areas is 0x02.

	This is usefull for catching things like:

		ptr = malloc(20);

		bptr = ptr+10;

		/* do something usefule with bptr */

		free(ptr);

		/* 
		 * now try to do something useful with bptr, it should
		 * be trashed enough that it would cause real problems
		 * and when you went to debug the problem it would be
		 * filled with 0x02's and you would then know to look 
		 * for something free'ing what bptr points to.
		 */
		

 3. Whenever a bstring(3)/string(3)/memory(3) function is called, it's 
    parameters are checked as follows:

	If they point somewhere in the malloc arena
		If the operation goes beyond requested malloc space
			call malloc_warning()

	This is usefull for catching things like:

		ptr = malloc(5);
		strcpy(ptr,"abcde");
			
	
 4. Malloc_warning() and malloc_fatal() are used when an error condition
    is detected.  If the error is severe, malloc_fatal is called.  
    Malloc_warning is used otherwise.  The decision about what is fatal
    and what is a warning was made somewhat arbitrarily.

    Warning messages include:

	Calling free with a bad pointer
        Calling a bstring/string/memory (3) function which will go beyond
	    the end of a malloc block (Note that the library function is
            not modified to refuse the operation.  If malloc warnings are
	    in the default IGNORE case, the operation will continue and 
	    at some point cause a real problem).

    Fatal errors are:

	Detectable corruption to the malloc chain.
	

 5. The operations to perform when an error is detected are specified at
    run time by the use of environment variables.

	MALLOC_WARN - specifies the warning error message handling
	MALLOC_FATAL - specifies the fatal error handling


	When one of these error conditions occur you will get an error
	message and the handler will execute based upon what setting
	is in the environment variables.  Currently understood settings
	are as follows:

		  0 - continue operations
		  1 - drop core and exit
		  2 - just exit
		  3 - drop core, but continue executing.  Core files will
	 		be placed into core.[PID].[counter] i.e: core.00123.001
		128 - dump malloc chain and continue
		129 - dump malloc chain, dump core, and exit
		130 - dump malloc chain, exit
		131 - dump malloc chain, dump core, continue processing
		

	There is an additional environment variable MALLOC_ERRFILE which
	is used to indicate the name of the file for error message output.

	For example, to set up the session to generate a core file for
	every malloc warning, to drop core and exit on a malloc fatal, and 
	to log all messages to the file "malloc_log" do the following:

		MALLOC_WARN=131
		MALLOC_FATAL=1
		MALLOC_ERRFILE=malloc_log

		export MALLOC_WARN MALLOC_FATAL MALLOC_ERRFILE

 6. The function malloc_dump() is available to dump the malloc chain whenever
    you might want.  It's only argument is a file descriptor to use to write
    the data.  Review the code if you need to know what data is printed.
SHAR_EOF
cat << \SHAR_EOF > calloc.c
#include <stdio.h>

char *
calloc(nelem,elsize)
	unsigned int 	  nelem;
	unsigned int 	  elsize;
{
	char		* malloc();
	char		* ptr;
	unsigned int	  size;

	size = elsize * nelem;

	if( (ptr = malloc(size)) != NULL)
	{
		memset(ptr,'\0',size);
	}

	return(ptr);
}


SHAR_EOF
cat << \SHAR_EOF > debug.h
/* static char SCCSID[] = "%W%   	%E%"; */
/************************************************************************/
/*									*/
/* this include sets up some macro functions which can be used while	*/
/* debugging the program, and then left in the code, but turned of by	*/
/* just not defining "DEBUG".  This way your production version of 	*/
/* the program will not be filled with bunches of debugging junk	*/
/*									*/
/************************************************************************/

#ifdef DEBUG

#if DEBUG == 1			/* if default level			*/
#undef DEBUG
#define DEBUG	100		/*   use level 100			*/
#endif

#include <stdio.h>

#define DEBUG0(val,str)\
				{\
				  if( DEBUG > val ) \
				    fprintf(stderr,"%s(%d): %s\n",\
						__FILE__,__LINE__,str);\
				}
#define DEBUG1(val,str,a1)\
			        {\
				  char _debugbuf[100];\
				  if( DEBUG > val )\
				   {\
				    sprintf(_debugbuf,str,a1);\
				    fprintf(stderr,"%s(%d): %s\n",\
						__FILE__,__LINE__,_debugbuf);\
				   }\
		       		}

#define DEBUG2(val,str,a1,a2)\
			        {\
				 char _debugbuf[100];\
				  if( DEBUG > val )\
				   {\
				    sprintf(_debugbuf,str,a1,a2);\
				    fprintf(stderr,"%s(%d): %s\n",\
						__FILE__,__LINE__,_debugbuf);\
				   }\
		       		}

#define DEBUG3(val,str,a1,a2,a3)\
			        {\
				  char _debugbuf[100];\
				  if( DEBUG > val )\
				   {\
				    sprintf(_debugbuf,str,a1,a2,a3);\
				    fprintf(stderr,"%s(%d): %s\n",\
						__FILE__,__LINE__,_debugbuf);\
				   }\
		       		}

#define DEBUG4(val,str,a1,a2,a3,a4)\
			         {\
				  char _debugbuf[100];\
				  if( DEBUG > val )\
				   {\
				    sprintf(_debugbuf,str,a1,a2,a3,a4);\
				    fprintf(stderr,"%s(%d): %s\n",\
						__FILE__,__LINE__,_debugbuf);\
				   }\
		       		}

#define DEBUG5(val,str,a1,a2,a3,a4,a5)\
			         {\
				  char _debugbuf[100];\
				  if( DEBUG > val )\
				   {\
				    sprintf(_debugbuf,str,a1,a2,a3,a4,a5);\
				    fprintf(stderr,"%s(%d): %s\n",\
						__FILE__,__LINE__,_debugbuf);\
				   }\
		       		}

#else

#define DEBUG0(val,s)
#define DEBUG1(val,s,a1)
#define DEBUG2(val,s,a1,a2)
#define DEBUG3(val,s,a1,a2,a3)
#define DEBUG4(val,s,a1,a2,a3,a4)
#define DEBUG5(val,s,a1,a2,a3,a4,a5)

#endif /* DEBUG */


SHAR_EOF
cat << \SHAR_EOF > design

This malloc library will emulate the standard malloc libraries but in
addition it will include special versions of the following routines which
perform checking of the arguments passed to verify that they do not
extend outside of a malloced area.

These modified routines include:

	bcopy, bzero, bcmp
	memcpy, memset, memcmp, memccpy, memchr
	strcpy, strncpy, strcat, strncat, strdup, strcmp, strncmp,
	strchr, strrchr, strpbrk, strspn, strcspn, strtok

These functions will only be different from the standard functions in 
that they will call routines in the malloc library to verify that the
data accessed by these routines does not overflow a malloc block. 

If an error is detected, the default action is to display a warning
and continue with the function.  In other words, it should match
the functionality of the library function with the exception that
the error message is printed.
SHAR_EOF
cat << \SHAR_EOF > dump.c
#include <stdio.h>
#include "malloc.h"
#include "toascii.h"

/*
 * Function:	malloc_dump()
 *
 * Purpose:	to dump a printed copy of the malloc chain and
 *		associated data elements
 *
 * Arguments:	fd	- file descriptor to write data to
 *
 * Returns:	nothing of any use
 *
 * Narrative:	Just print out all the junk
 *
 * Notes:	This function is implemented using low level calls because
 * 		of the likelyhood that the malloc tree is damaged when it
 *		is called.  (Lots of things in the c library use malloc and
 *		we don't want to get into a catch-22).
 *
 * Mod History:	
 *   90/01/24	cpcahil		Initial revision.
 */

#define ERRSTR	"I/O Error on malloc dump file descriptor\n"

#define WRITEOUT(fd,str,len)	if( write(fd,str,len) != len ) \
				{ \
					(void) write(2,ERRSTR,strlen(ERRSTR));\
					exit(120); \
				}

malloc_dump(fd)
	FILE		* fd;
{
	char			  buffer[512];
	int			  i;
	extern char		* malloc_data_end;
	extern char		* malloc_data_start;
	extern struct mlist 	* malloc_end;
	extern struct mlist	  malloc_start;
	struct mlist 		* ptr;

	WRITEOUT(fd,"MALLOC CHAIN:\n",14);
	WRITEOUT(fd,"-------------------- START ----------------\n",44);

	for(i=0; i < 80; i++)
	{
		buffer[i] = ' ';
	}

	for(ptr = &malloc_start; ptr; ptr = ptr->next)
	{
		(void) toascii(buffer,	 ptr,		 8,	B_HEX,	'0');
		(void) toascii(buffer+9, ptr->next,	 8,	B_HEX,	'0');
		(void) toascii(buffer+18,ptr->prev,	 8,	B_HEX,	'0');
		(void) toascii(buffer+27,ptr->flag,	10,	B_HEX,	'0');
		(void) toascii(buffer+38,ptr->s.size,	 8,	B_DEC,	' ');
		(void) toascii(buffer+47,ptr->s.size,	 8,	B_HEX,	'0');
		(void) toascii(buffer+57,ptr->data,	 8,	B_HEX,	'0');
		buffer[46] = '(';
		buffer[55] = ')';
		buffer[65] = '\n';
		WRITEOUT(fd,buffer,66);
	}
	WRITEOUT(fd,"-------------------- DONE -----------------\n",44);

	WRITEOUT(fd,"Malloc start:      ",19);
	(void) toascii(buffer, &malloc_start, 8, B_HEX, '0');
	buffer[8] = '\n';
	WRITEOUT(fd,buffer,9);

	WRITEOUT(fd,"Malloc end:        ", 19);
	(void) toascii(buffer, malloc_end, 8, B_HEX, '0');
	buffer[8] = '\n';
	WRITEOUT(fd,buffer,9);

	WRITEOUT(fd,"Malloc data start: ", 19);
	(void) toascii(buffer, malloc_data_start, 8, B_HEX, '0');
	buffer[8] = '\n';
	WRITEOUT(fd,buffer,9);

	WRITEOUT(fd,"Malloc data end:   ", 19);
	(void) toascii(buffer, malloc_data_end, 8, B_HEX, '0');
	buffer[8] = '\n';
	WRITEOUT(fd,buffer,9);
	
} /* malloc_dump(... */
SHAR_EOF
cat << \SHAR_EOF > free.c
#include <stdio.h>
#include "malloc.h"
#include "debug.h"

void
free(cptr)
	char	* cptr;
{
	int			  i;
	extern struct mlist	* malloc_end;
	extern char		* malloc_data_end;
	extern char		* malloc_data_start;
	struct mlist		* oldptr;
	struct mlist		* ptr;

	/*
	 * First verify that cptr is within the malloc region...
	 */
	if( cptr < malloc_data_start || cptr > malloc_data_end )
	{
		malloc_warning("Free called with invalid address...");
		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_INUSE))	     ||
	    (ptr->prev && (ptr->prev->next != ptr) ) ||
	    (ptr->next && (ptr->next->prev != ptr) ) ||
	    ((ptr->next == NULL) && (ptr->prev == NULL)) )
	{
		malloc_warning(
		   "Free: called with invalid pointer or corrupted chain");
		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_warning( "data overrun in malloced region" );
			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,ptr->s.size);

}
SHAR_EOF
cat << \SHAR_EOF > func.hdr
/*
 * Function:	malloc_in_arena()
 *
 * Purpose:	to verify address is within malloc arena.
 *
 * Arguments:	ptr	- pointer to verify
 *
 * Returns:	TRUE	- if pointer is within malloc area
 *		FALSE	- otherwise
 *
 * Narrative:
 *   IF pointer is >= malloc area start AND <= malloc area end
 *      return TRUE
 *   ELSE
 *      return FALSE
 *
 * Mod History:	
 *   90/01/24	cpcahil		Initial revision.
 */
SHAR_EOF
cat << \SHAR_EOF > malloc.c

#include <stdio.h>
#include <fcntl.h>
#include "malloc.h"
#include "toascii.h"

char		* malloc_data_start;
char		* malloc_data_end;
struct mlist 	* malloc_end;
int		  malloc_errfd = 2;
int		  malloc_fatal_level = M_HANDLE_CORE;
struct mlist	  malloc_start;
int		  malloc_warn_level;

char * malloc(size)
	unsigned int	  size;
{
	char		* cptr;
	char		* getenv();
	int		  i;
	void		  malloc_split();
	unsigned int	  need;
	struct mlist	* oldptr;
	struct mlist	* ptr;
	unsigned int	  rest;
	char		* sbrk();
	struct mlist	* tptr;

	/*
	 * If this is the first call to malloc...
	 */
	if( malloc_data_start == (char *) 0 )
	{
		malloc_data_start = sbrk(0);
		malloc_data_end = malloc_data_start;
		malloc_start.s.size = 0;
		malloc_end = &malloc_start;
		
		if( (cptr=getenv("MALLOC_WARN")) != NULL )
		{
			malloc_warn_level = atoi(cptr);
		}

		if( (cptr=getenv("MALLOC_FATAL")) != NULL)
		{
			malloc_fatal_level = atoi(cptr);
		}
		if( (cptr=getenv("MALLOC_ERRFILE")) != NULL)
		{
			i  = open(cptr,O_CREAT|O_TRUNC|O_WRONLY,0666);
			if( i != -1)
			{
				malloc_errfd = i;
			}
		}
	}

	/*
	 * if they ask for zero bytes, give em at least 1...
	 */
	if( size == 0 )
	{
		size = 1;
	}

	/*
	 * Now look for a free area of memory of size bytes...
	 */
	oldptr = NULL;
	for(ptr = &malloc_start; ; 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_fatal("Corrupted malloc chain.");
			}
			oldptr = ptr;
		}
		else if( oldptr != malloc_end )
		{
			/*
			 * This should never happen.  If it does, then
			 * we got a real problem.
			 */
			malloc_fatal("Corrupted malloc chain 2.");
		}
		

		/*
		 * if this element is already in use...
		 */
		if( ptr && ((ptr->flag & M_INUSE) != 0) )
		{
			continue;
		}

		/*
		 * if there isn't room for this block..
		 */
		if( ptr && (ptr->s.size < size) )
		{
			continue;
		}

		/*
		 * If ptr is null, we have run out of memory and must sbrk more
		 */
		if( ptr == NULL )
		{
			need = (size + M_SIZE) * (size > 100*1024 ? 1:2);
			if( need < M_BLOCKSIZE )
			{
				need = M_BLOCKSIZE;
			}
			else if( need & (M_BLOCKSIZE-1) )
			{
				need &= ~(M_BLOCKSIZE-1);
				need += M_BLOCKSIZE;
			}
			ptr = (struct mlist *) sbrk(need);
			if( ptr == (struct mlist *) -1 )
			{
				malloc_fatal("Can't allocate any more memory");
			}
			malloc_data_end = sbrk(0);

			ptr->prev   = oldptr;
			ptr->next   = (struct mlist *) 0;
			ptr->s.size = need - M_SIZE;
			ptr->flag  = M_MAGIC;

			oldptr->next = ptr;
			malloc_end = ptr;


		} /* if( ptr ==... */

		/*
	 	 * Now ptr points to a memory location that can store
		 * this data, so lets go to work.
		 */

		ptr->r_size = size;		/* save requested size	*/
		ptr->flag |= M_INUSE;

		/*
	 	 * split off unneeded data area in this block, if possible...
		 */
		malloc_split(ptr);

		/*
		 * just to make sure that noone is misusing malloced
	 	 * memory without initializing it, lets set it to
		 * all '\01's.  We call local_memset() because memset()
		 * may be checking for malloc'd ptrs and this isn't
		 * a malloc'd ptr yet.
		 */
		malloc_memset(ptr->data,M_FILL,ptr->s.size);

		return( ptr->data);

	} /* for(... */

} /* malloc(... */

/*
 * Function:	malloc_split()
 *
 * Purpose:	to split a malloc segment if there is enough room at the
 *		end of the segment that isn't being used
 *
 * Arguments:	ptr	- pointer to segment to split
 *
 * Returns:	nothing of any use.
 *
 * Narrative:
 *
 * Mod History:	
 *   90/01/27	cpcahil		Initial revision.
 */
void
malloc_split(ptr)
	struct mlist		* ptr;
{
	extern struct mlist	* malloc_end;
	int			  rest;
	int			  size;
	struct mlist		* tptr;
	void			  malloc_join();

	size = ptr->r_size;

	/*
	 * roundup size to the appropriate boundry
	 */

	M_ROUNDUP(size);

	/*
	 * figure out how much room is left in the array.
	 * if there is enough room, create a new mlist 
	 *     structure there.
	 */

	if( ptr->s.size > size )
	{
		rest = ptr->s.size - size;
	}
	else
	{
		rest = 0;
	}
	
	if( rest > (M_SIZE+M_RND) )
	{
		tptr = (struct mlist *) (ptr->data+size);
		tptr->prev = ptr;
		tptr->next = ptr->next;
		tptr->flag = M_MAGIC;
		tptr->s.size = rest - M_SIZE;

		/*
		 * If possible, join this segment with the next one
		 */

		malloc_join(tptr, tptr->next,0,0);

		if( tptr->next )
		{
			tptr->next->prev = tptr;
		}

		malloc_memset(tptr->data,M_FREE_FILL, tptr->s.size);

		ptr->next = tptr;
		ptr->s.size = size;

		if( malloc_end == ptr )
		{
			malloc_end = tptr;
		}
	}

} /* malloc_split(... */

/*
 * Function:	malloc_join()
 *
 * Purpose:	to join two malloc segments together (if possible)
 *
 * Arguments:	ptr	- pointer to segment to join to.
 * 		nextptr	- pointer to next segment to join to ptr.
 *
 * Returns:	nothing of any values.
 *
 * Narrative:
 *
 * Mod History:	
 *   90/01/27	cpcahil		Initial revision.
 */
void
malloc_join(ptr,nextptr, inuse_override, fill_flag)
	struct mlist	* ptr;
	struct mlist	* nextptr;
	int		  inuse_override;
	int		  fill_flag;
{
	unsigned int	  newsize;

	if( 	ptr     && ! (inuse_override || (ptr->flag & M_INUSE)) && 
		nextptr && ! (nextptr->flag & M_INUSE) &&
		((ptr->data+ptr->s.size) == (char *) nextptr) )
	{
		if( malloc_end == nextptr )
		{
			malloc_end = ptr;
		}
		ptr->next = nextptr->next;
		newsize = nextptr->s.size + M_SIZE;

		/*
		 * if we are to fill and this segment is in use,
		 *   fill in with M_FILL newly added space...
	 	 */

		if(fill_flag && (ptr->flag & M_INUSE) )
		{
			malloc_memset(ptr->data+ptr->s.size,
				      M_FILL, nextptr->s.size + M_SIZE);
		}

		ptr->s.size += newsize;
		if( ptr->next )
		{
			ptr->next->prev = ptr;
		}
	}

} /* malloc_join(... */


/*
 * The following mess is just to ensure that the versions of these functions in
 * the current library are included (to make sure that we don't accidentaly get 
 * the libc versions. (This is the lazy man's -u ld directive)
 */

int free();
int strcmp();
int memcmp();
char	* realloc();

int		(*malloc_int_funcs[])() =
{
	free,
	strcmp,
	memcmp,
};

char		* (*malloc_char_star_funcs[])() =
{
	realloc,
};

/*
 * This is malloc's own memset which is used without checking the parameters.
 */

malloc_memset(ptr,byte,len)
	char		* ptr;
	char		  byte;
	int		  len;
{

	while(len-- > 0)
	{
		*ptr++ = byte;
	}

} /* malloc_memset(... */

void
malloc_fatal(str)
	char		* str;
{
	char		  errbuf[128];
	int		  len;
	extern int	  malloc_fatal_level;
	char		* s;
	char		* t;

	s = errbuf;
	t = "Fatal error: ";
	while( *s = *t++)
	{
		s++;
	}
	t = str;
	while( *s = *t++)
	{
		s++;
	}
	*(s++) = '\n';

	if( write(malloc_errfd,errbuf,(s-errbuf)) != (s-errbuf))
	{
		write(2,"I/O error to error file\n",24);
		exit(110);
	}
	malloc_err_handler(malloc_fatal_level);

} /* malloc_fatal(... */

void
malloc_warning(str)
	char		* str;
{
	char		  errbuf[128];
	int		  len;
	extern int	  malloc_warn_level;
	char		* s;
	char		* t;

	s = errbuf;
	t = "Warning: ";
	while( *s = *t++)
	{
		s++;
	}
	t = str;
	while( *s = *t++)
	{
		s++;
	}
	*(s++) = '\n';

	if( write(malloc_errfd,errbuf,(s-errbuf)) != (s-errbuf))
	{
		write(2,"I/O error to error file\n",24);
		exit(110);
	}

	malloc_err_handler(malloc_warn_level);

} /* malloc_warning(... */

malloc_err_handler(level)
{
	if( malloc_warn_level & M_HANDLE_DUMP )
	{
		malloc_dump(malloc_errfd);
	}

	switch( malloc_warn_level & ~M_HANDLE_DUMP )
	{
		/*
		 * If we are to drop a core file and exit
		 */
		case M_HANDLE_ABORT:
			abort();
			break;

		/*
		 * If we are to exit..
		 */
		case M_HANDLE_EXIT:
			exit(200);
			break;

		/*
		 * If we are to dump a core, but keep going on our merry way
		 */
		case M_HANDLE_CORE:
			{
				int	  pid;
				int	  rpid;

				/*
				 * fork so child can abort (and dump core)
				 */
				if( (pid = fork()) == 0 )
				{
					write(2,"Child dumping core\n",19);
					abort();
				}

				/*
 				 * wait for child to finish dumping core
				 */
				while( (rpid=wait((int *)0)) != pid)
				{
				}

				/*
				 * Move core file to core.pid.cnt so 
				 * multiple cores don't overwrite each
				 * other.
				 */
				if( access("core",0) == 0 )
				{
					static int	  corecnt;
					char	  	  filenam[32];
					filenam[0] = 'c';
					filenam[1] = 'o';
					filenam[2] = 'r';
					filenam[3] = 'e';
					filenam[4] = '.';
					(void)toascii(filenam+5,getpid(),
						5, B_DEC, '0');
					filenam[10] = '.';
					(void)toascii(filenam+11,corecnt++,
						3, B_DEC, '0');
					filenam[14] = '\0';
					(void) unlink(filenam);
					if( link("core",filenam) == 0)
					{
						(void) unlink("core");
					}
				}
			}


		/* 
		 * If we are to just ignore the error and keep on processing
		 */
		case M_HANDLE_IGNORE:
			break;

	} /* switch(... */

} /* malloc_err_handler(... */
SHAR_EOF
cat << \SHAR_EOF > malloc.h

struct mlist
{
	struct mlist	* next;			/* next entry in chain	*/
	struct mlist	* prev;			/* prev entry in chain	*/
	int	 	  flag;			/* inuse flag		*/
	unsigned int	  r_size;		/* requested size	*/
	union
	{
		unsigned int	  size;		/* actual size		*/
		double		  unused_just_for_alignment;
	} s;
	char		  data[4];
};

#define M_SIZE		((int)(char *)((struct mlist *)0)->data)
#define M_RND		0x08

#define M_INUSE 	0x01
#define M_MAGIC 	0x03156100

#define M_BLOCKSIZE	(1024*8)

#define M_FILL		'\01'
#define M_FREE_FILL	'\02'

#define M_ROUNDUP(size)	{\
				if( size & (M_RND-1) ) \
				{ \
					size &= ~(M_RND-1); \
					size += M_RND; \
				} \
			}

/*
 * Malloc warning/fatal error handler defines...
 */
#define M_HANDLE_DUMP	0x80  /* 128 */
#define M_HANDLE_IGNORE	0
#define M_HANDLE_ABORT	1
#define M_HANDLE_EXIT	2
#define M_HANDLE_CORE	3
	

void malloc_warning();
void malloc_fatal();
void malloc_check_data();
void malloc_check_str();
void malloc_verify();
SHAR_EOF
cat << \SHAR_EOF > malloc_chk.c

#include <stdio.h>
#include "malloc.h"
#include "debug.h"

extern struct mlist	  malloc_start;
extern struct mlist	* malloc_end;
extern char		* malloc_data_start;
extern char		* malloc_data_end;

/*
 * Function:	malloc_in_arena()
 *
 * Purpose:	to verify address is within malloc arena.
 *
 * Arguments:	ptr	- pointer to verify
 *
 * Returns:	TRUE	- if pointer is within malloc area
 *		FALSE	- otherwise
 *
 * Narrative:
 *   IF pointer is >= malloc area start AND <= malloc area end
 *      return TRUE
 *   ELSE
 *      return FALSE
 *
 * Mod History:	
 *   90/01/24	cpcahil		Initial revision.
 */
int
malloc_in_arena(ptr)
	char	* ptr;
{
	extern char	* malloc_data_start;
	extern char	* malloc_data_end;
	int		  rtn = 0;

	if( ptr >= malloc_data_start && ptr <= malloc_data_end )
	{
		rtn = 1;
	}
	
	return(rtn);
}

/*
 * Function:	malloc_check_str()
 *
 * Arguments:	func	- name of function calling this routine
 *		str	- pointer to area to check
 *
 * Purpose:	to verify that if str is within the malloc arena, the data 
 *		it points to does not extend beyond the applicable region.
 *
 * Returns:	Nothing of any use (function is void).
 *
 * Narrative:
 *   IF pointer is within malloc arena
 *      determin length of string
 *      call malloc_verify() to verify data is withing applicable region
 *   return 
 *
 * Mod History:	
 *   90/01/24	cpcahil		Initial revision.
 *   90/01/29	cpcahil		Added code to ignore recursive calls.
 */
void
malloc_check_str(func,str)
	char		* func;
	char		* str;
{
	static int	  layers;
	register char	* s;

	if( (layers++ == 0) &&  malloc_in_arena(str) )
	{
		for( s=str; *s; s++)
		{
		}
		
		malloc_verify(func,str,s-str+1);
	}

	layers--;
}

/*
 * Function:	malloc_check_data()
 *
 * Arguments:	func	- name of function calling this routine
 *		ptr	- pointer to area to check
 *		len 	- length to verify
 *
 * Purpose:	to verify that if ptr is within the malloc arena, the data 
 *		it points to does not extend beyond the applicable region.
 *
 * Returns:	Nothing of any use (function is void).
 *
 * Narrative:
 *   IF pointer is within malloc arena
 *      call malloc_verify() to verify data is withing applicable region
 *   return 
 *
 * Mod History:	
 *   90/01/24	cpcahil		Initial revision.
 *   90/01/29	cpcahil		Added code to ignore recursive calls.
 */
void
malloc_check_data(func,ptr,len)
	char		* func;
	char		* ptr;
	int		  len;
{
	static int	  layers;

	if( layers++ == 0 )
	{
		DEBUG3(40,"malloc_check_data(%s,0x%x,%d) called...",
			func,ptr,len);
		if( malloc_in_arena(ptr) )
		{
			DEBUG0(10,"pointer in malloc arena, verifying...");
			malloc_verify(func,ptr,len);
		}
	}

	layers--;
}

/*
 * Function:	malloc_verify()
 *
 * Arguments:	func	- name of function calling the malloc check routines
 *		ptr	- pointer to area to check
 *		len 	- length to verify
 *
 * Purpose:	to verify that the data ptr points to does not extend beyond
 *		the applicable malloc region.  This function is only called 
 *		if it has been determined that ptr points into the malloc arena.
 *
 * Returns:	Nothing of any use (function is void).
 *
 * Narrative:
 *
 * Mod History:	
 *   90/01/24	cpcahil		Initial revision.
 */
void
malloc_verify(func,ptr,len)
	char		* func;
	char		* ptr;
	int		  len;
{
	extern struct mlist 	  malloc_start;
	extern struct mlist	* malloc_end;
	struct mlist		* mptr;
	
	DEBUG3(40,"malloc_verify(%s,0x%x,%d) called...", func,ptr,len);
	/*
	 * Find the malloc block that includes this pointer
	 */
	mptr = &malloc_start;
	while( mptr && 
		! (((char *)mptr < ptr) && ((mptr->data+mptr->s.size) > ptr) ) )
	{
		mptr = mptr->next;
	}

	/*
	 * if ptr was not in a malloc block, it must be part of
	 *    some direct sbrk() stuff, so just return.
	 */
	if( ! mptr )
	{
		DEBUG1(10,"ptr (0x%x) not found in malloc search", ptr);
		return;
	}
	
	/*
 	 * Now we have a valid malloc block that contains the indicated
	 * pointer.  We must verify that it is withing the requested block
	 * size (as opposed to the real block size which is rounded up to
	 * allow for correct alignment).
	 */

	DEBUG4(60,"Checking  0x%x-0x%x, 0x%x-0x%x",
			ptr, ptr+len, mptr->data, mptr->data+mptr->r_size);
	
	if( (ptr < mptr->data) || ((ptr+len) > (mptr->data+mptr->r_size)) )
	{
		char errstr[512];
		DEBUG4(0,"pointer not within region 0x%x-0x%x, 0x%x-0x%x",
			ptr, ptr+len, mptr->data, mptr->data+mptr->r_size);
		sprintf(errstr,"%s %s, %s 0x%x %s '%.2x %.2x %.2x %.2x %.2x'",
			"Malloced memory overflow detected in function",
			func, "address:", ptr, "Data: ",
			*ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4));
		malloc_fatal(errstr);
	}

	return;
}

SHAR_EOF
cat << \SHAR_EOF > memory.c

char *
memccpy(ptr1, ptr2, ch, len)
	register char	* ptr1;
	register char	* ptr2;
	int		  len;
	int		  ch;
{
	register int	  i;
	char		* rtn;

	/*
	 * I know that the assignment could be done in the following, but
	 * I wanted to perform a check before any assignment, so first I 
	 * determine the length, check the pointers and then do the assignment.
	 */
	for( i=0; (i < len) && (ptr2[i] != ch); i++)
	{
	}

	malloc_check_data("memccpy", ptr1, i);
	malloc_check_data("memccpy", ptr2, i);

	/*
	 * if we found the character...
 	 */

	if( i < len )
	{
		rtn = ptr1+i+1;
	}
	else
	{
		rtn = (char *) 0;
	}

 	while( i-- )
	{
		*(ptr1++) = *(ptr2++);
	}
	
	return(rtn);
}

char *
memchr(ptr1,ch,len)
	register char	* ptr1;
	register int	  ch;
	int		  len;
{
	int		  i;

	for( i=0; (i < len) && (ptr1[i] != ch); i++)
	{
	}

	malloc_check_data("memchr", ptr1, i);

	if( i < len )
	{
		return( ptr1+i );
	}
	else
	{
		return( (char *) 0);	
	}
}

char *
memcpy(ptr1, ptr2, len)
	register char	* ptr1;
	register char	* ptr2;
	register int	  len;
{
	char		* rtn = ptr1;

	malloc_check_data("memcpy", ptr1, len);
	malloc_check_data("memcpy", ptr2, len);
	
	while( len-- )
	{
		*(ptr1++) = *(ptr2++);
	}
	return(rtn);
}

int
memcmp(ptr1, ptr2, len)
	register char	* ptr1;
	register char	* ptr2;
	register int	  len;
{
	malloc_check_data("memcpy", ptr1, len);
	malloc_check_data("memcpy", ptr2, len);

	while( --len && (*ptr1 == *ptr2) )
	{
		ptr1++;
		ptr2++;
	}
	return( *ptr1 - *ptr2 );
}

char * 
memset(ptr1, ch, len)
	register char	* ptr1;
	register int	  ch;
	register int	  len;
{
	char		* rtn = ptr1;

	malloc_check_data("memcpy", ptr1, len);

	while( len-- )
	{
		*(ptr1++) = ch;
	}

	return(rtn);
}

bcopy(ptr2,ptr1,len)
{
	memcpy(ptr1,ptr2,len);
}

bzero(ptr1,len)
{
	memset(ptr1,'\0',len);
}

int
bcmp(ptr2, ptr1, len)
{
	return( memcmp(ptr1,ptr2,len) );
}
SHAR_EOF
cat << \SHAR_EOF > pgm.c
#include <stdio.h>

main(argc,argv)
	int	  argc;
	char	* argv[];
{
	int	  i;
	char	* malloc();
	char	* ptr[10];
	char	* realloc();

	ptr[0] = malloc(2034);	
	printf("malloc() returned 0x%x\n", ptr[0]);

	ptr[1] = malloc(2034);	
	printf("malloc() returned 0x%x\n", ptr[1]);

	ptr[2] = malloc(14321);	
	printf("malloc() returned 0x%x\n", ptr[2]);

	malloc_dump(2);
	getchar();

	memset(ptr[1],' ',2034);
	ptr[5] = realloc(ptr[1],20300);
/*	fprintf(stderr,"value at position 200 after realloc: 0x%x\n", ptr[3][200]);
*/

	malloc_dump(2);
	getchar();

	memset(ptr[2],'\0',14321);
	free(ptr[2]);

	malloc_dump(2);
	getchar();

	free(ptr[0]);

	malloc_dump(2);
	getchar();

	ptr[3] = malloc(2034);	
	printf("malloc() returned 0x%x\n", ptr[3]);

	ptr[4] = malloc(234);	
	printf("malloc() returned 0x%x\n", ptr[4]);

	malloc_dump(2);
	getchar();

	for(i=0; i < 10; i++)
	{
		free(ptr[i]);
		malloc_dump(2);
	}

}
SHAR_EOF
cat << \SHAR_EOF > realloc.c
#include <stdio.h>
#include "malloc.h"

char *
realloc(cptr,size)
	char			* cptr;
	unsigned int		  size;
{
	int			  i;
	char			* malloc();
	extern struct mlist 	* malloc_end;
	extern char		* malloc_data_end;
	extern char		* malloc_data_start;
	char			* new_cptr;
	struct mlist		* ptr;
	int			  r_size;
	int			  rest;
	struct mlist		* tptr;

	/*
	 * First verify that cptr is within the malloc region...
	 */
	if( cptr < malloc_data_start || cptr > malloc_data_end )
	{
		malloc_warning("realloc called with invalid address...");
		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_INUSE))	     ||
	    (ptr->prev && (ptr->prev->next != ptr) ) ||
	    (ptr->next && (ptr->next->prev != ptr) ) ||
	    ((ptr->next == NULL) && (ptr->prev == NULL)) )
	{
		malloc_warning(
		   "realloc: called with invalid pointer or corrupted chain");
		return(NULL);
	}

	r_size = size;

	M_ROUNDUP(size);

	if( size > ptr->s.size )
	{
		malloc_join(ptr,ptr->next,1,1);
	}

	if( size < ptr->s.size )
	{
		rest = ptr->s.size - size;
	}
	else
	{
		/*
		 * else we can't combine it, so lets allocate a new chunk,
		 * copy the data and free the old chunk...
		 */
		new_cptr = malloc(size);

		if( new_cptr == (char *) 0)
		{
			return(new_cptr);
		}

		if( r_size < ptr->r_size )
		{
			i = r_size;
		}
		else
		{
			i = ptr->r_size;
		}
		memcpy(new_cptr,ptr->data,i);
		free(cptr);
		return(new_cptr);

	} /* else... */

	/*
	 * save amount of real data in new segment (this will be used in the
	 * memset later) and then save requested size of this segment.
	 */

	if( ptr->r_size < r_size )
	{
		i = ptr->r_size;
	}
	else
	{
		i = r_size;
	}

	ptr->r_size = r_size;

	/*
	 * split off extra free space at end of this segment, if possible...
	 */

	malloc_split(ptr);

	malloc_memset( ptr->data+i, M_FILL, ptr->s.size - i);
		
	return(ptr->data);

} /* realloc(... */




SHAR_EOF
cat << \SHAR_EOF > string.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include "malloc.h"

int	malloc_checking = 0;

char *
strcat(str1,str2)
	register char	* str1;
	register char	* str2;
{
	char		* rtn;
	int	  len;

	/* 
	 * check pointers agains malloc region.  The malloc* functions
	 * will properly handle the case where a pointer does not
	 * point into malloc space.
	 */
	malloc_checking = 1;

	len = strlen(str2);
	malloc_check_str("strcat", str2);

	len += strlen(str1) + 1;
	malloc_checking = 0;

	malloc_check_data("strcat", str1, len);

	rtn = str1;

	while( *str1 )
	{
		str1++;
	}
	
	while( (*str1 = *str2) != '\0' )
	{
		str1++;
		str2++;
	}
	
	return(rtn);
}

char *
strdup(str1)
	register char	* str1;
{
	char		* malloc();
	char		* rtn;
	register char	* str2;

	malloc_check_str("strdup", str1);

	rtn = str2 = malloc(strlen(str1));

	while( (*str2 = *str1) != '\0' )
	{
		str1++;
		str2++;
	}

	return(rtn);
}

char *
strncat(str1,str2,len)
	register char	* str1;
	register char	* str2;
	register int	  len;
{
	int 		  len1;
	int 		  len2;
	char		* rtn;

	malloc_checking = 1;

	len2 = strlen(str2) + 1;
	len1 = strlen(str1);

	malloc_checking = 0;

	malloc_check_data("strncat", str2,len2);

	if( (len+1) < len2 )
	{
		len1 += len + 1;
	}
	else
	{
		len1 += len2;
	}
	malloc_check_data("strncat", str1, len1);

	rtn = str1;

	while( *str1 )
	{
		str1++;
	}

	while( len-- && ((*str1++ = *str2++) != '\0') )
	{
	}
	
	if( ! len )
	{
		*str1 = '\0';
	}

	return(rtn);
}

int
strcmp(str1,str2)
	register char	* str1;
	register char	* str2;
{
	malloc_check_str("strcmp", str1);
	malloc_check_str("strcmp", str2);

	while( *str1 && (*str1 == *str2) )
	{
		str1++;
		str2++;
	}
	return( *str1 - *str2 );
}

int
strncmp(str1,str2,len)
	register char	* str1;
	register char	* str2;
	register int	  len;
{
	malloc_check_str("strncmp", str1);
	malloc_check_str("strncmp", str2);

	while( --len && *str1 && (*str1 == *str2) )
	{
		str1++;
		str2++;
	}
	return( *str1 - *str2 );
}

char *
strcpy(str1,str2)
	register char	* str1;
	register char	* str2;
{
	char		* rtn;
	int		  len;

	malloc_checking = 1;
	len = strlen(str2) + 1;
	malloc_checking = 0;

	malloc_check_data("strcpy", str1, len);
	malloc_check_data("strcpy", str2, len);

	rtn = str1;

	while( (*str1++ = *str2++) != '\0')
	{
	}

	return(rtn);
}

char *
strncpy(str1,str2,len)
	register char	* str1;
	register char	* str2;
	register int	  len;
{
	int		  i;
	extern int	  malloc_checking;
	char		* rtn;

	malloc_check_data("strncpy", str1, len);

	malloc_checking = 1;
	i = strlen(str2);
	malloc_checking = 0;

	if( i > len )
	{
		i = len;
	}

	malloc_check_data("strncpy", str2, i);

	rtn = str1;

	while(len-- && (*str1++ = *str2++) != '\0')
	{
	}
	while( len-- )
	{
		*str1++ = '\0';
	}

	return(rtn);
}

int
strlen(str1)
	register char	* str1;
{
	register char	* s;

	if(! malloc_checking )
	{
		malloc_check_str("strlen", str1);
	}

	for( s = str1; *s; s++)
	{
	}

	return( s - str1 );
}

char *
strchr(str1,c)
	register char	* str1;
	register int	  c;
{
	malloc_check_str("strchr", str1);

	while( *str1 && (*str1 != (char) c) )
	{
		str1++;
	}

	if(! *str1 )
	{
		str1 = (char *) 0;
	}

	return(str1);
}

char *
strrchr(str1,c)
	register char	* str1;
	register int	  c;
{
	register char	* rtn = (char *) 0;

	malloc_check_str("strrchr", str1);

	while( *str1 )
	{
		if(*str1 == (char) c )
		{
			rtn = str1;
		}
		str1++;
	}

	return(rtn);
}

char *
index(str1,c)
{
	return( strchr(str1,c) );
}

char *
rindex(str1,c)
{
	return( strrchr(str1,c) );
}

char *
strpbrk(str1,str2)
	register char	* str1;
	register char	* str2;
{
	register char	* tmp;

	malloc_check_str("strpbrk", str1);
	malloc_check_str("strpbrk", str2);

	while(*str1)
	{
		for( tmp=str2; *tmp && *tmp != *str1; tmp++)
		{
		}
		if( *tmp )
		{
			break;
		}
		str1++;
	}

	if( ! *str1 )
	{
		str1 = (char *) 0;
	}

	return(str1);
}

int
strspn(str1,str2)
	register char	* str1;
	register char	* str2;
{
	register char	* tmp;
	char		* orig = str1;

	malloc_check_str("strspn", str1);
	malloc_check_str("strspn", str2);

	while(*str1)
	{
		for( tmp=str2; *tmp && *tmp != *str1; tmp++)
		{
		}
		if(! *tmp )
		{
			break;
		}
		str1++;
	}

	return( (int) (str1 - orig) );
}

int
strcspn(str1,str2)
	register char	* str1;
	register char	* str2;
{
	register char	* tmp;
	char		* orig = str1;

	malloc_check_str("strcspn", str1);
	malloc_check_str("strcspn", str2);

	while(*str1)
	{
		for( tmp=str2; *tmp && *tmp != *str1; tmp++)
		{
		}
		if( *tmp )
		{
			break;
		}
		str1++;
	}

	return( (int) (str1 - orig) );
}

/*
 * strtok() source taken from that posted to comp.lang.c by Chris Torek
 * in Jan 1990.
 */

/*
 * Copyright (c) 1989 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
 * Get next token from string s (NULL on 2nd, 3rd, etc. calls),
 * where tokens are nonempty strings separated by runs of
 * chars from delim.  Writes NULs into s to end tokens.  delim need not
 * remain constant from call to call.
 *
 * Modified by cpc: 	changed variable names to conform with naming
 *			conventions used in rest of code.  Added malloc pointer
 *			check calls.
 */
char *
strtok(str1, str2)
	char 	* str1;
	char	* str2;
{
	static char 	* last;
	char		* strtoken();

	if( str1 )
	{
		malloc_check_str("strtok", str1);
		last = str1;
	}
	malloc_check_str("strtok", str2);

	return (strtoken(&last, str2, 1));
}


/*
 * Get next token from string *stringp, where tokens are (possibly empty)
 * strings separated by characters from delim.  Tokens are separated
 * by exactly one delimiter iff the skip parameter is false; otherwise
 * they are separated by runs of characters from delim, because we
 * skip over any initial `delim' characters.
 *
 * Writes NULs into the string at *stringp to end tokens.
 * delim will usually, but need not, remain constant from call to call.
 * On return, *stringp points past the last NUL written (if there might
 * be further tokens), or is NULL (if there are definitely no more tokens).
 *
 * If *stringp is NULL, strtoken returns NULL.
 */
char *
strtoken(stringp, delim, skip)
	register char **stringp;
	register char *delim;
	int skip;
{
	register char *s;
	register char *spanp;
	register int c, sc;
	char *tok;

	if ((s = *stringp) == NULL)
		return (NULL);

	if (skip) {
		/*
		 * Skip (span) leading delimiters (s += strspn(s, delim)).
		 */
	cont:
		c = *s;
		for (spanp = delim; (sc = *spanp++) != 0;) {
			if (c == sc) {
				s++;
				goto cont;
			}
		}
		if (c == 0) {		/* no token found */
			*stringp = NULL;
			return (NULL);
		}
	}

	/*
	 * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
	 * Note that delim must have one NUL; we stop if we see that, too.
	 */
	for (tok = s;;) {
		c = *s++;
		spanp = delim;
		do {
			if ((sc = *spanp++) == c) {
				if (c == 0)
					s = NULL;
				else
					s[-1] = 0;
				*stringp = s;
				return (tok);
			}
		} while (sc != 0);
	}
	/* NOTREACHED */
}
SHAR_EOF
cat << \SHAR_EOF > testmalloc.c
/* NOT copyright by SoftQuad Inc. -- msb, 1988 */
#ifndef lint
static char *SQ_SccsId = "@(#)mtest3.c	1.2 88/08/25";
#endif
#include <stdio.h>
/*
** looptest.c -- intensive allocator tester 
**
** Usage:  looptest
**
** History:
**	4-Feb-1987 rtech!daveb 
*/

# ifdef i386
# define SYS5
# endif

# ifdef SYS5
# define random	rand
# else
# include <sys/vadvise.h>
# endif

# include <stdio.h>
# include <signal.h>
# include <setjmp.h>

# define MAXITER	1000000		/* main loop iterations */
# define MAXOBJS	1000		/* objects in pool */
# define BIGOBJ		90000		/* max size of a big object */
# define TINYOBJ	80		/* max size of a small object */
# define BIGMOD		100		/* 1 in BIGMOD is a BIGOBJ */
# define STATMOD	10000		/* interation interval for status */

main( argc, argv )
int argc;
char **argv;
{
	register int **objs;		/* array of objects */
	register int *sizes;		/* array of object sizes */
	register int n;			/* iteration counter */
	register int i;			/* object index */
	register int size;		/* object size */
	register int r;			/* random number */

	int objmax;			/* max size this iteration */
	int cnt;			/* number of allocated objects */
	int nm = 0;			/* number of mallocs */
	int nre = 0;			/* number of reallocs */
	int nal;			/* number of allocated objects */
	int nfre;			/* number of free list objects */
	long alm;			/* memory in allocated objects */
	long frem;			/* memory in free list */
	long startsize;			/* size at loop start */
	long endsize;			/* size at loop exit */
	long maxiter = 0;		/* real max # iterations */

	extern char end;		/* memory before heap */
	char *calloc();
	char *malloc();
	char *sbrk();
	long atol();

# ifndef SYS5
	/* your milage may vary... */
	vadvise( VA_ANOM );
# endif

	if (argc > 1)
		maxiter = atol (argv[1]);
	if (maxiter <= 0)
		maxiter = MAXITER;

	printf("MAXITER %d MAXOBJS %d ", maxiter, MAXOBJS );
	printf("BIGOBJ %d, TINYOBJ %d, nbig/ntiny 1/%d\n",
	BIGOBJ, TINYOBJ, BIGMOD );
	fflush( stdout );

	if( NULL == (objs = (int **)calloc( MAXOBJS, sizeof( *objs ) ) ) )
	{
		fprintf(stderr, "Can't allocate memory for objs array\n");
		exit(1);
	}

	if( NULL == ( sizes = (int *)calloc( MAXOBJS, sizeof( *sizes ) ) ) )
	{
		fprintf(stderr, "Can't allocate memory for sizes array\n");
		exit(1);
	}

	/* as per recent discussion on net.lang.c, calloc does not 
	** necessarily fill in NULL pointers...
	*/
	for( i = 0; i < MAXOBJS; i++ )
		objs[ i ] = NULL;

	startsize = sbrk(0) - &end;
	printf( "Memory use at start: %d bytes\n", startsize );
	fflush(stdout);

	printf("Starting the test...\n");
	fflush(stdout);
	for( n = 0; n < maxiter ; n++ )
	{
		if( !(n % STATMOD) )
		{
			printf("%d iterations\n", n);
			fflush(stdout);
		}

		/* determine object of interst and it's size */

		r = random();
		objmax = ( r % BIGMOD ) ? TINYOBJ : BIGOBJ;
		size = r % objmax;
		i = r % (MAXOBJS - 1);

		/* either replace the object of get a new one */

		if( objs[ i ] == NULL )
		{
			objs[ i ] = (int *)malloc( size );
			nm++;
		}
		else
		{
			/* don't keep bigger objects around */
			if( size > sizes[ i ] )
			{
				objs[ i ] = (int *)realloc( objs[ i ], size );
				nre++;
			}
			else
			{
				free( objs[ i ] );
				objs[ i ] = (int *)malloc( size );
				nm++;
			}
		}

		sizes[ i ] = size;
		if( objs[ i ] == NULL )
		{
			printf("\nCouldn't allocate %d byte object!\n", 
				size );
			break;
		}
	} /* for() */

	printf( "\n" );
	cnt = 0;
	for( i = 0; i < MAXOBJS; i++ )
		if( objs[ i ] )
			cnt++;

	printf( "Did %d iterations, %d objects, %d mallocs, %d reallocs\n",
		n, cnt, nm, nre );
	printf( "Memory use at end: %d bytes\n", sbrk(0) - &end );
	fflush( stdout );

	/* free all the objects */
	for( i = 0; i < MAXOBJS; i++ )
		if( objs[ i ] != NULL )
			free( objs[ i ] );

	endsize = sbrk(0) - &end;
	printf( "Memory use after free: %d bytes\n", endsize );
	fflush( stdout );

	if( startsize != endsize )
		printf("startsize %d != endsize %d\n", startsize, endsize );

	free( objs );
	free( sizes );

	malloc_dump();
	exit( 0 );
}

SHAR_EOF
cat << \SHAR_EOF > testmem.c

#include <stdio.h>

/* 
 * These tests test the memory functions...
 */

main()
{
	char	  buffer[500];
	int	  exitval = 0;
	char	* memccpy();
	char	* memchr();
	char	* ptr1;
	char	* ptr2;

	fprintf(stdout,"Begining memory(3) tests...\n");

	/*
 	 * test memccpy()...
 	 */

	ptr1 = "abcdefghijklmn";	

	ptr2 = memccpy(buffer,ptr1,'d',3);

	if( ptr2 != (char *) 0)
	{
		fprintf(stdout,"memccpy() failed to use passed length\n");
		exitval++;
	}


	ptr2 = memccpy(buffer,ptr1,'d',4);

	if( ptr2 != (buffer+4) )
	{
		fprintf(stdout,"memccpy() failed to find byte in data\n");
		exitval++;
	}

	/*
	 * Test memchr()...
	 */

	ptr1 = "abcdefghijklmn";

	ptr2 = memchr(ptr1,'c',10);
	
	if( ptr2 != (ptr1+2) )
	{
		fprintf(stdout,"memchr() failed to find byte in data\n");
		exitval++;
	}

	ptr2 = memchr(ptr1,'j',10);
	
	if( ptr2 != (ptr1+9) )
	{
		fprintf(stdout,"memchr() failed to find byte in data\n");
		exitval++;
	}

	ptr2 = memchr(ptr1,'k',10);
	
	if( ptr2 != (char *) 0)
	{
		fprintf(stdout,"memchr() failed to obey length argument\n");
		exitval++;
	}

	fprintf(stdout,"Memory tests complete!\n");
	exit(exitval);

}
SHAR_EOF
cat << \SHAR_EOF > toascii.c
#include "toascii.h"

/*
 * Function:	toascii()
 *
 * Purpose:	to convert an integer to an ascii display string
 *
 * Arguments:	buf	- place to put the 
 *		val	- integer to convert
 *		len	- length of output field (0 if just enough to hold data)
 *		base	- base for number conversion (only works for base <= 16)
 *		fill	- fill char when len > # digits
 *
 * Returns:	length of string
 *
 * Narrative:	IF fill character is non-blank
 *		    Determine base
 *		        If base is HEX
 *		            add "0x" to begining of string
 *		        IF base is OCTAL
 *		            add "0" to begining of string
 *
 *		While value is greater than zero
 *		    use val % base as index into xlation str to get cur char
 *		    divide val by base
 *
 *		Determine fill-in length
 *
 *		Fill in fill chars
 *
 *		Copy in number
 *		
 *
 * Mod History:	
 *   90/01/24	cpcahil		Initial revision.
 */

#define T_LEN 10

int
toascii(buf,val,len,base,fill)
	int	  base;
	char	* buf;
	char	  fill;
	int	  len;
	int	  val;
	
{
	char	* bufstart = buf;
	int	  i = T_LEN;
	char	* xbuf = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	char	  tbuf[T_LEN];

	/*
	 * if we are filling with non-blanks, make sure the
	 * proper start string is added
	 */
	if( fill != ' ' )
	{
		switch(base)
		{
			case B_HEX:
				*(buf++) = '0';
				*(buf++) = 'x';
				if( len )
				{
					len -= 2;
				}
				break;
			case B_OCTAL:
				*(buf++) = fill;
				if( len )
				{
					len--;
				}
				break;
			default:
				break;
		}
	}

	while( val > 0 )
	{
		tbuf[--i] = xbuf[val % base];
		val = val / base;
	}

	if( len )
	{
		len -= (T_LEN - i);

		if( len > 0 )
		{
			while(len-- > 0)
			{
				*(buf++) = fill;
			}
		}
		else
		{
			/* 
			 * string is too long so we must truncate
			 * off some characters.  We do this the easiest
			 * way by just incrementing i.  This means the
			 * most significant digits are lost.
			 */
			while( len++ < 0 )
			{
				i++;
			}
		}
	}

	while( i < T_LEN )
	{
		*(buf++) = tbuf[i++];
	}

	return( (int) (buf - bufstart) );

} /* toascii(... */

SHAR_EOF
cat << \SHAR_EOF > toascii.h
#define B_BIN	 2
#define B_DEC	10
#define B_HEX	16
#define B_OCTAL	 8
SHAR_EOF
#	End of shell archive
exit 0
-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+