/*
 *	fhist - file history and comparison tools
 *	Copyright (C) 1991-1994, 1998, 2000-2002 Peter Miller;
 *	All rights reserved.
 *
 *	Derived from a work
 *	Copyright (C) 1990 David I. Bell.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 * MANIFEST: functions to isolate malloc
 *
 * Malloc interludes which allow for releasing of all used memory blocks.
 * Just call cm_reset whenever you want to free all allocated memory.
 * This is convenient when reinitializing a program which allocates memory.
 *
 * Note: You must make sure that old stale pointers are not referenced,
 * such as static pointers which were 0 when the program started but
 * won't be later.  In other words, to be safe you should not assume any
 * variables have been initialized, even for zero values.
 */

#include <ac/errno.h>
#include <ac/stdlib.h>
#include <ac/stddef.h>

#include <cmalloc.h>
#include <error.h>

#undef malloc
#undef realloc
#undef free
#undef calloc


#define	MAGIC	54928347L

/*
 * This is to protect DOS.
 * On a 16-bit machine, this will be 65535,
 * exactly right for small-memory-model doc programs.
 *
 * but on a 32-bit machine, like most unixes,
 * this is essentially infinity.
 */
#define BIG ((unsigned int)-1)


typedef	struct chunk	CHUNK;
struct chunk
{
    CHUNK	*next;		/* next chunk in chain */
    CHUNK	*prev;		/* previous chunk in chain */
    long	magic;		/* magic value */
    double	data[1];	/* data storage (varying size) */
};

#define	CHUNKSIZE(size) (sizeof(CHUNK) + (size) - sizeof(double))

#define	DPTOCP(dp) \
	((CHUNK *)(void*)(((char *)(dp)) - offsetof(CHUNK, data[0])))


static	CHUNK	head = {&head, &head};


/*
 * Actual calls to malloc, realloc, calloc, and free.
 * Users must be sure not to intermix these calls with the ones above.
 */

void *
r_alloc(size_t size)
{
    void            *result;

    if (size > BIG)
    {
	errno = EINVAL;
	return 0;
    }
    errno = 0;
    result = malloc((unsigned int) size);
    if (!result && !errno)
	errno = ENOMEM;
    return result;
}


void *
r_realloc(void *dp, size_t size)
{
    void            *result;

    if (size > BIG)
    {
	errno = EINVAL;
	return 0;
    }
    errno = 0;
    result = realloc(dp, (unsigned int) size);
    if (!result && !errno)
	errno = ENOMEM;
    return result;
}


void *
r_realloc_and_check(void *a, size_t b)
{
    void            *result;

    result = r_realloc(a, b);
    if (!result)
	nfatal_raw("realloc(%lu)", (unsigned long)b);
    return result;
}


void
r_free(void *dp)
{
    free(dp);
}


void *
r_alloc_and_check(size_t n)
{
    void            *result;

    result = r_alloc(n);
    if (!result)
       	nfatal_raw("malloc(%lu)", (unsigned long)n);
    return result;
}


/*
 * Allocate some memory.
 */

void *
cm_alloc(size_t size)
{
    CHUNK           *cp;

    if (size > BIG)
    {
	errno = EINVAL;
	return 0;
    }
    cp = (CHUNK *)r_alloc((size_t)CHUNKSIZE(size));
    if (!cp)
	return 0;
    cp->magic = MAGIC;
    cp->next = head.next;
    cp->next->prev = cp;
    cp->prev = &head;
    head.next = cp;
    return (void *)cp->data;
}


void *
cm_alloc_and_check(size_t n)
{
    void            *result;

    result = cm_alloc(n);
    if (!result)
	nfatal_raw("malloc(%lu)", (unsigned long)n);
    return result;
}


/*
 * Reallocate memory.
 */

void *
cm_realloc(void *dp, size_t size)
{
    CHUNK           *cp;

    if (!dp || size > BIG)
    {
	errno = EINVAL;
	return 0;
    }
    cp = DPTOCP(dp);
    if (cp->magic != MAGIC)
    {
	error_raw
	(
    	    "%s: %d: bad realloc %08lX (bug)",
    	    __FILE__,
    	    __LINE__,
    	    (unsigned long)dp
	);
	errno = EINVAL;
	return 0;
    }
    cp = (CHUNK *)r_realloc(cp, (size_t)CHUNKSIZE(size));
    if (!cp)
	return 0;
    cp->next->prev = cp;
    cp->prev->next = cp;
    return (void *)cp->data;
}


void *
cm_realloc_and_check(void *a, size_t b)
{
    void            *result;

    result = cm_realloc(a, b);
    if (!result)
	nfatal_raw("malloc(%lu)", (unsigned long)b);
    return result;
}


/*
 * Allocate zeroed memory.
 */

void *
cm_calloc(size_t nelem, size_t elsize)
{
    size_t          totalsize;
    CHUNK           *cp;

    if (nelem > BIG || elsize > BIG)
    {
	errno = EINVAL;
	return 0;
    }
    totalsize = CHUNKSIZE(nelem * elsize);
    if (totalsize > BIG)
    {
	errno = EINVAL;
	return 0;
    }
    cp = (CHUNK *)r_alloc(totalsize);
    if (!cp)
	return 0;
    cp->magic = MAGIC;
    cp->next = head.next;
    cp->next->prev = cp;
    cp->prev = &head;
    head.next = cp;
    return (void *)cp->data;
}


void *
cm_calloc_and_check(size_t a, size_t b)
{
    void            *result;

    result = cm_calloc(a, b);
    if (!result)
	nfatal_raw("malloc(%lu)", (unsigned long)b);
    return result;
}


/*
 * Free memory.
 */

void
cm_free(void *dp)
{
    CHUNK           *cp;

    if (!dp)
	return;
    cp = DPTOCP(dp);
    if (cp->magic != MAGIC)
    {
	error_raw
	(
    	    "%s: %d: bad free %08lX (bug)",
    	    __FILE__,
    	    __LINE__,
    	    (unsigned long)dp
	);
	return;
    }
    cp->prev->next = cp->next;
    cp->next->prev = cp->prev;
    cp->next = 0;
    cp->prev = 0;
    cp->magic = 0;
    r_free(cp);
}


/*
 * Reset the whole memory arena.
 * This frees all memory allocated since the last cm_reset call.
 */

void
cm_reset(void)
{
    CHUNK           *cp;

    while (head.next != &head)
    {
	cp = head.next;
	if (cp->magic != MAGIC)
	{
	    error_raw
	    (
		"%s: %d: bad cm_reset %08lX (bug)",
		__FILE__,
		__LINE__,
		(unsigned long)cp
	    );
	    break;
	}
	head.next = cp->next;
	cp->next = 0;
	cp->prev = 0;
	cp->magic = 0;
	r_free(cp);
    }
    head.next = &head;
    head.prev = &head;
}


syntax highlighted by Code2HTML, v. 0.9.1