#include <stdlib.h>
#include <stdio.h>
#include "../include/aftersteplib.h"

#ifdef DEBUG_ALLOCS
#undef safemalloc
#undef calloc
#undef realloc
#undef free
typedef struct mem
  {
    struct mem *next;
    char *fname;
    int line;
    int length;
    void *ptr;
    char freed;
  }
mem;

static mem *first_mem = NULL;
static int allocations = 0;
static int reallocations = 0;
static int deallocations = 0;
static int max_allocations = 0;

void *
countmalloc (char *fname, int line, int length)
{
  mem *m = (mem *) malloc (sizeof (mem));
  memset (m, 0, sizeof (mem));
  m->next = first_mem;
  first_mem = m;
  m->fname = fname;
  m->line = line;
  m->length = length;
  m->ptr = safemalloc (length);
  m->freed = 0;
  if ((++allocations) - deallocations > max_allocations)
    max_allocations = allocations - deallocations;
  return m->ptr;
}

void *
countcalloc (char *fname, int line, int nrecords, int length)
{
  mem *m = (mem *) malloc (sizeof (mem));
  memset (m, 0, sizeof (mem));
  m->next = first_mem;
  first_mem = m;
  m->fname = fname;
  m->line = line;
  m->length = length;
  m->ptr = calloc (nrecords, length);
  m->freed = 0;
  if ((++allocations) - deallocations > max_allocations)
    max_allocations = allocations - deallocations;
  return m->ptr;
}

void *
countrealloc (char *fname, int line, void *ptr, int length)
{
  if (ptr != NULL)
    {
      mem *m;
      for (m = first_mem; m != NULL; m = m->next)
	if (m->ptr == ptr)
	  break;
      if (m == NULL)
	{
	  printf ("%s:mem to realloc not in list!\n", __FUNCTION__);
	  printf ("%s:called from %s:%d\n", __FUNCTION__, fname, line);
	  return NULL;
	  /* exit (1); */
	}
      m->fname = fname;
      m->line = line;
      m->length = length;
      m->ptr = realloc (ptr, length);
      m->freed = 0;
      ptr = m->ptr;
      reallocations++;
    }
  else
    {
      ptr = countmalloc (fname, line, length);
    }

  return ptr;
}

void
countfree (char *fname, int line, void *ptr)
{
  mem *m1;
  mem *m2;

  /* scan for an invalid allocation list */
  {
    int q;
    mem *mmm;
    for (q = 0, mmm = first_mem; mmm != NULL && q < 10000; mmm = mmm->next, q++);
    if (q == 10000)
      {
	fprintf (stderr, "%s:alloc list invalid\n", __FUNCTION__);
	exit (1);
      }
  }
  for (m1 = m2 = first_mem; m1 != NULL; m2 = m1, m1 = m1->next)
    if (m1->ptr == ptr)
      break;

  if (m1 == NULL)
    {
      printf ("%s:mem to free not in list!\n", __FUNCTION__);
      printf ("%s:called from %s:%d\n", __FUNCTION__, fname, line);
      /* exit (1); */
      return;
    }

#if 0
  if (m1->freed > 0)
    {
      printf ("%s:mem already freed %d time(s)!\n", __FUNCTION__, m1->freed);
      printf ("%s:freed from %s:%d\n", __FUNCTION__, (*m1).fname, (*m1).line);
      printf ("%s:called from %s:%d\n", __FUNCTION__, fname, line);
      /* exit (1); */
    }
  else
    free (m1->ptr);
  m1->freed++;
  m1->fname = fname;
  m1->line = line;
#else
  free (m1->ptr);
  if (m1 == first_mem)
    first_mem = m1->next;
  else
    m2->next = m1->next;
  free (m1);
#endif
  {
    int q;
    mem *mmm;
    for (q = 0, mmm = first_mem; mmm != NULL && q < 10000; mmm = mmm->next, q++);
    if (q == 10000)
      {
	fprintf (stderr, "%s:alloc list invalid\n", __FUNCTION__);
	exit (1);
      }
  }
  deallocations++;
}

#include <ctype.h>
void
print_unfreed_mem (void)
{
  mem *m;

  printf ("%s\n", __FUNCTION__);
  printf ("Total   allocs: %d\n", allocations);
  printf ("Total reallocs: %d\n", reallocations);
  printf ("Total deallocs: %d\n", deallocations);
  printf ("Max allocs at any one time: %d\n", max_allocations);
  for (m = first_mem; m != NULL; m = m->next)
    if (m->freed == 0)
      {
	printf (" %20s:%4d:%d", m->fname, m->line, m->length);
	if ((mystrncasecmp (m->fname, "stripcpy", 8) == 0) || isalnum (*((char *) m->ptr)))
	  printf (" '%s'", (char *) m->ptr);
	printf ("\n");
      }
}
#endif /*DEBUG_ALLOCS */
/***********************************************************************
 *
 *  Procedure:
 *	safemalloc - mallocs specified space or exits if there's a 
 *		     problem
 *
 ***********************************************************************/
char *
safemalloc (int length)
{
  char *ptr;

  if (length <= 0)
    length = 1;

  ptr = malloc (length);
  if (ptr == (char *) 0)
    {
      fprintf (stderr, "malloc of %d bytes failed. Exiting\n", length);
      exit (1);
    }
  return ptr;
}
