#define SAFEMALLOC_C

#include "main.h"

/* **********************************************************************
 * this file and it's extern and include files has a LARGE ammount of
 * preprocessor use in it ... be aware */

static unsigned int malloc_count = 0;
#ifdef EXTRA_MALLOC_WRAPPER
static unsigned int malloc_size = 0;
static malloc_lookup *malref_start[MALLOC_MAX_TYPES] = {0};

static void addto_malloc_ref(malloc_lookup *tmp, unsigned int type)
{
 assert(tmp);
 
 if ((tmp->next = malref_start[tmp->type]))
   tmp->next->prev = tmp;

 tmp->prev = NULL;
 malref_start[type] = tmp;

 malloc_size += (tmp->size + sizeof(malloc_lookup));
}

static malloc_lookup *_find_malloc_ref(void *ptr, malloc_lookup *tmp)
{
 while (tmp)
 {
  if (tmp->mem_ref == ptr)
    return (tmp);

  tmp = tmp->next;
 }
 
 return (NULL);
}

static malloc_lookup *find_malloc_ref(void *ptr, unsigned int type,
                                      const char *file_ref,
                                      unsigned int line_ref,
                                      const char *calling_func_name)
{
 malloc_lookup *tmp = NULL;

 assert(type < MALLOC_MAX_TYPES);
 
 if ((tmp = _find_malloc_ref(ptr, malref_start[type])))
   return (tmp);
 else
 {
  unsigned int count = 0;

  while (count < MALLOC_MAX_TYPES)
  {
   if ((tmp = _find_malloc_ref(ptr, malref_start[count])))
    {
     fprintf(stderr, " (%s) wrong section: \n"
             " orig = file %s, line %u, type %u\n"
             " found = file %s, line %u, type %u\n",
             calling_func_name,
             tmp->file_ref, tmp->line_ref, tmp->type,
             file_ref, line_ref, type);
     
     return (tmp);
    }

   ++count;
  }
  
  fprintf(stderr, "(%s) block not found = file %s, line %u, type %u\n",
          calling_func_name, file_ref, line_ref, type);
  
  return (NULL);
 }
}

#ifdef USE_POINTER_COMPARES
static malloc_lookup *_find_malloc_block(const void *ptr, malloc_lookup *tmp)
{
 while (tmp)
 {
  if (!PTR_LESS(ptr, tmp->mem_ref) &&
      (PTR_DIFF(ptr, tmp->mem_ref) < tmp->size))
    return (tmp);

  tmp = tmp->next;
 }
 
 return (NULL);
}

static malloc_lookup *find_malloc_block(const void *ptr, unsigned int type,
                                        const char *file_ref,
                                        unsigned int line_ref,
                                        const char *calling_func_name)
{
 malloc_lookup *tmp = NULL;

 assert(type < MALLOC_MAX_TYPES);
 
 if ((tmp = _find_malloc_block(ptr, malref_start[type])))
   return (tmp);
 else
 {
  unsigned int count = 0;

  while (count < MALLOC_MAX_TYPES)
  {
   if ((tmp = _find_malloc_block(ptr, malref_start[count])))
   {
    fprintf(stderr, " (%s) wrong section: \n"
            " orig  = file %s, line %u, type %u\n"
            " found = file %s, line %u, type %u\n",
            calling_func_name,
            tmp->file_ref, tmp->line_ref, tmp->type, file_ref, line_ref, type);
    
    return (tmp);
   }
   ++count;
  }
  
  fprintf(stderr, "(%s) block not found = file %s, line %u, type %u\n",
          calling_func_name, file_ref, line_ref, type);
  
  return (NULL);
 }
}
#endif

static int remove_malloc_ref(void *ptr, unsigned int type,
                             const char *file_ref, unsigned int line_ref)
{
 malloc_lookup *tmp = NULL;
 
 assert(ptr && (type < MALLOC_MAX_TYPES) && file_ref && line_ref);
 
 if ((tmp = find_malloc_ref(ptr, type, file_ref, line_ref,
                            "remove_malloc_ref")))
 {
  if (tmp->prev)
    tmp->prev->next = tmp->next;
  else
    malref_start[type] = tmp->next;
  
  if (tmp->next)
    tmp->next->prev = tmp->prev;
  
  malloc_size -= (tmp->size + sizeof(malloc_lookup));
  memset(ptr, MALLOC_FREE_GARBAGE, tmp->size);
  memset(tmp, MALLOC_FREE_GARBAGE, sizeof(malloc_lookup));
  free (tmp);
  
  return (TRUE);
 }

 return (FALSE);
}
#endif

void *safecalloc(size_t nmemb, size_t size, unsigned int type,
                 const char *file_ref, unsigned int line_ref)
{
 void *ptr = NULL;

 BTRACE("safecalloc");
 
 ptr = calloc (nmemb, size);
  
#ifdef EXTRA_MALLOC_WRAPPER
 {
  malloc_lookup *tmp = malloc (sizeof(malloc_lookup));
  
  if (ptr)
    if (tmp)
    {
     tmp->size = (nmemb * size);
     tmp->type = type;
     tmp->file_ref = file_ref;
     tmp->line_ref = line_ref;
     tmp->mem_ref = ptr;
     addto_malloc_ref(tmp, type);
    }
    else
    {
     free (ptr);
     BTRACE("safecalloc (end)");
     return (NULL);
    }
 }
#else
 IGNORE_PARAMETER(type && file_ref && line_ref); 
#endif

 if (ptr)
 {
  malloc_count++;
 /* *******************************
  * no memset garbage needed, as calloc garantee's to
  * memset everything to 0's */
  BTRACE("safecalloc (worked)");
 }
 else
  BTRACE("safecalloc (end)");
 
 return (ptr);
} 

void *safemalloc(size_t size, unsigned int type,
                 const char *file_ref, unsigned int line_ref)
{
 void *ptr = NULL;

 BTRACE("safemalloc");

 ptr = malloc (size);

#ifdef EXTRA_MALLOC_WRAPPER
 {
  malloc_lookup *tmp = malloc (sizeof(malloc_lookup));
  
  if (ptr)
    if (tmp)
    {
     tmp->size = size;
     tmp->type = type;
     tmp->file_ref = file_ref;
     tmp->line_ref = line_ref;
     tmp->mem_ref = ptr;
     addto_malloc_ref(tmp, type);
    }
    else
    {
     free (ptr);
     BTRACE("safemalloc (end)");
     return (NULL);
    }
 }
#else
 IGNORE_PARAMETER(type && file_ref && line_ref); 
#endif

 if (ptr)
 {
#ifdef NDEBUG
  memset(ptr, 0, size); /* it should work more often */
#else
  memset(ptr, MALLOC_MALLOC_GARBAGE, size);
#endif
  malloc_count++;
  BTRACE("safemalloc (worked)");
 }
 else
   BTRACE("safemalloc (end)");

 return (ptr);
}

void safefree(void *ptr, unsigned int type,
              const char *file_ref, unsigned int line_ref)
{
 BTRACE("safefree");
 assert(malloc_count > 0);
 malloc_count--;
#ifndef EXTRA_MALLOC_WRAPPER
 IGNORE_PARAMETER(type && file_ref && line_ref);
#else
 if (remove_malloc_ref(ptr, type, file_ref, line_ref))
#endif
   free (ptr);
 BTRACE("safefree (worked)");
}

#ifdef EXTRA_MALLOC_WRAPPER
void malloc_no_members(unsigned int type,
                       const char *file_ref, unsigned int line_ref)
{
 if (malref_start[type])
 {
  fprintf(stderr, "malloc not empty file %s, line %u, type %u.\n",
          file_ref, line_ref, type);
  
  assert(0);
 }
}

# ifdef USE_POINTER_COMPARES
int malloc_valid(const void *ptr, size_t size, unsigned int type,
                 const char *file_ref, unsigned int line_ref)             
{
 malloc_lookup *tmp = NULL;

 assert(ptr && (type < MALLOC_MAX_TYPES) && file_ref && line_ref);
 
 if ((tmp = find_malloc_block(ptr, type, file_ref, line_ref,
                              "malloc_valid")))
 {
  if ((tmp->size - PTR_DIFF(ptr, tmp->mem_ref)) >= size)
    return (TRUE);

  fprintf(stderr, " (malloc_valid) wrong size: \n"
          " malloc = file %s, line %u, type %u, bytes left = %u\n"
          " valid  = file %s, line %u, type %u, bytes asked = %u\n",
          tmp->file_ref, tmp->line_ref, tmp->type,
          (unsigned int) (tmp->size - PTR_DIFF(ptr, tmp->mem_ref)),
          file_ref, line_ref, type, (unsigned int) size);
 }
  
 return (FALSE);
}
# endif             
#endif

#ifdef TALKER_MAIN_H
void malloc_count_show(player *p, char *str)
{
#ifdef EXTRA_MALLOC_WRAPPER
 char *end_str = next_parameter(str, ' ');
 malloc_lookup *tmp = NULL;
 unsigned int count = 0;
 unsigned int type = 0;
 unsigned int size = 0;

 if (end_str)
 {
  *end_str++ = 0;
  type = atol(str);
  size = atol(end_str);
 }
 else
 {
  TELL_FORMAT(p, "<malloc_type> <minimum size>");
  fvtell_player(SYSTEM_T(p), " Types go from 0 to %u.\n", MALLOC_MAX_TYPES);
 }
 
 if (type <= MALLOC_MAX_TYPES)
 {
  tmp = malref_start[type];
  fvtell_player(SYSTEM_T(p), " Starting type (%u).\n", type);
 }
 else
   fvtell_player(SYSTEM_T(p), " Types go from 0 to %u.\n", MALLOC_MAX_TYPES);
 
 while (tmp)
 {
  ++count;
  if (tmp->size > size)
    fvtell_player(SYSTEM_T(p), " (%05u) is size %06u from %s, at line %u.\n",
                  count, (unsigned int) tmp->size, tmp->file_ref,
                  tmp->line_ref);
  tmp = tmp->next;
 }
 
 fvtell_player(SYSTEM_T(p), " Total malloc size is now at: ^B%d^N\n",
               malloc_size);
#else
 IGNORE_PARAMETER(str);
#endif
 fvtell_player(SYSTEM_T(p), " Total malloc count is now at: ^B%d^N\n",
               malloc_count);
  pager(p, 1);
}
#endif
