#include "args.h"
/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

#ifndef lint
static const char *rcsid = "$Header: /usr/local/src/mm/mm-0.94/mm/RCS/debug.c,v 1.1 2005/05/28 22:27:51 beebe Exp $";
#endif

#include "args.h"
#include <stdio.h>
#include "config.h"			/* site configuration file */
#include "osfiles.h"			/* standard system header files */
#include "compat.h"			/* compatibility macros */


/*
 * memdebug:
 * variable to control memory debugging.  It must be declared in the user
 * program.
 * if memdebug == 1, then action is always taken.
 * if memdebug == 0, then no action is taken.
 * if memdebug == -1, then the user is asked.
 */

extern int memdebug;

static int ask ARGS((const char *str));
void maybe_abort ARGS((const char *str));

#ifdef MDEBUG

#if HAVE_STDC
#include <stddef.h>
#include <stdlib.h>
#else
#if defined(sun) || defined(ultrix)
extern char *malloc ARGS((size_t));
extern char *realloc ARGS((void *, size_t));
#else
extern void *malloc ARGS((size_t));
extern void *realloc ARGS((void *, size_t));
#endif
#endif

static char *check_range ARGS((char *cp));
static char *set_range_check ARGS((char *cp, int size));
static void m_delete ARGS((char *cp));
static void m_insert ARGS((char *cp));

void *dcalloc ARGS((int nelem, int elsize));
void *dmalloc ARGS((int size));
void *drealloc ARGS((void *bp, int size));
void *safe_realloc ARGS((void *cp, int size));
void dfree ARGS((void *cp));

#define min(x,y) ((x) < (y) ? (x) : (y))
#define RANGE	 "ABCDEFGHIJKLMNOP"
#if defined(__alpha)
/* In order to preserve correct memory alignment on 64-bit machines,
we need to set INTSIZE to the size of a pointer; sizeof(RANGE) (== 16)
is already a multiple of that. */
#define INTSIZE  sizeof(void*)
#else
#define INTSIZE  sizeof(int)
#endif
#define LONGSIZE sizeof(long)
#define RSIZE    sizeof(RANGE)
#define RFRONT   min((RSIZE/2),LONGSIZE)
#define RBACK    min((RSIZE-RFRONT),LONGSIZE)

void *
dmalloc(size)
int size;
{
    void *cp;

    cp = (void *)malloc(size + RSIZE + INTSIZE);

    if (cp) {
	cp = set_range_check((char*)cp, size);
	m_insert(cp);
    }
    return(cp);
}

void *
dcalloc(nelem, elsize)
int nelem, elsize;
{
    void *cp;

    cp = dmalloc(nelem * elsize);
    if (cp)
	bzero(cp, nelem * elsize);
    return(cp);
}

void *
drealloc(bp,size)
void *bp;
int size;
{
    void *cp;

    if (bp == NULL) {
	maybe_abort("Freeing NULL pointer");
    }
    else {
	m_delete(bp);
	cp = check_range((char*)bp);
    }
    cp = realloc(cp, size + RSIZE + INTSIZE);
    if (cp) {
	cp = set_range_check((char*)cp, size);
	m_insert(cp);
    }
    return(cp);
}

void
dfree(cp)
void *cp;
{
    if (cp == NULL)
	maybe_abort("Freeing NULL pointer");
    else {
	m_delete(cp);
	cp = check_range((char*)cp);
    }
    free(cp);
}

static char *
set_range_check(cp,size)
char *cp;
int size;
{
    register int i;
    int tmp = size;

    for(i = 0; i < INTSIZE; i++) {	/* set the size in the string */
	cp[i] = tmp & 0xff;
	tmp >>= 8;
    }
    cp += INTSIZE;			/* skip the size */

    for(i = 0; i < RFRONT; i++)		/* set the front of the range check */
	cp[i] = RANGE[i];		/* string */

    cp += RFRONT;			/* skip the front range check */

    for(i = 0; i < RBACK; i++)		/* set the back odf the range check */
	cp[i+size] = RANGE[i+RFRONT];

    return(cp);
}

static char *
check_range(cp)
char *cp;
{
    register char *bp = cp - RFRONT - INTSIZE;
    char *xp = bp;
    register int i;
    int size = 0;

    for(i = 0 ; i < INTSIZE; i++) {	/* get the size out of the string */
	size <<= 8;
	size |= bp[INTSIZE-i-1] & 0xff;
    }
    bp += INTSIZE;

    for(i = 0; i < RFRONT; i++)		/* check front range check */
	if (bp[i] != RANGE[i]) {
	    maybe_abort("leftside malloc buffer overrun");
	    break;
	}
    bp += RFRONT;			/* skip front range check */

    for(i = 0; i < RBACK; i++)		/* check back rnage check */
	if (bp[i+size] != RANGE[i+RFRONT]) {
	    maybe_abort("rightside malloc buffer overrun");
	    break;
	}
    return(xp);
}


#define BUCKETS 10000
static char *m_used[BUCKETS];

static void
m_insert(cp)
register char *cp;
{
    register int i;

    for(i = 0; i < BUCKETS; i++)
	if (m_used[i] == 0) {
	    m_used[i] = cp;
	    return;
	}
}

static void
m_delete(cp)
register char *cp;
{
    register int i;

    for(i = 0; i < BUCKETS; i++)
	if (m_used[i] == cp) {
	    m_used[i] = 0;
	    return;
	}
    maybe_abort("Freeing unmalloc'ed pointer");
}

void
m_init() {
    register int i;
    for(i = 0; i < BUCKETS; i++)
	m_used[i] = 0;
}

void
m_done() {
#ifdef undef
    register int i,j;

    for(i = 0; i < BUCKETS; i++)
	if (m_used[i] != 0) {
	    if (memdebug) {
		if (j == 0)
		    fprintf(stderr,"unfree'ed buffers, indices: ");
		fprintf(stderr,"%d, ", i);
		j++;
	    }
	}
    if (j)
	fprintf(stderr,"\n");
    if (j)
	maybe_abort("Unfree'ed malloc buffers");
#endif
}

void
m_checkranges()
{
    int i;

    for ( i = 0; i < BUCKETS; i++)
	if (m_used[i])
	    check_range(m_used[i]);
}

#endif /* MDEBUG */

void
#if HAVE_STDC
maybe_abort(const char *str)
#else /* K&R style */
maybe_abort(str)
const char *str;
#endif /* HAVE_STDC */
{
    if (memdebug == 0)
	return;
    fprintf(stderr,"%s\n",str);
    if (memdebug == 1)
	abort();
    if (memdebug == -1)
	if (ask("Abort? "))
	    abort();
}

static int
#if HAVE_STDC
ask(const char *str)
#else /* K&R style */
ask(str)
const char *str;
#endif /* HAVE_STDC */
{
    char buf[100];
    FILE *in;
    int fd;

    fd = dup(fileno(stdin));
    in = fdopen(fd, "r");
    while(1) {
	fprintf(stderr,str);
	fflush(stderr);
	if (fgets(buf, 99, in) == NULL)	/* EOF? */
	    return(0);
	if (buf[0] == 'n' || buf[0] == 'N') {
	    fclose(in);
	    return(0);
	}
	if (buf[0] == 'y' || buf[0] == 'Y') {
	    fclose(in);
	    return(1);
	}
	fprintf(stderr,"please answer y/n.\n");
    }
}
