pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
pennmush/po/
pennmush/win32/msvc.net/
pennmush/win32/msvc6/
/**
 * \file memcheck.c
 *
 * \brief A simple memory allocation tracker for PennMUSH.
 *
 * This code isn't usually compiled in, but it's handy to debug
 * memory leaks sometimes.
 *
 *
 */
#include "config.h"
#include "conf.h"
#include "copyrite.h"

#include <stdlib.h>
#include <string.h>


#include "externs.h"
#include "dbdefs.h"
#include "mymalloc.h"
#include "log.h"
#include "confmagic.h"

typedef struct mem_check MEM;

/** A linked list for storing memory allocation counts */
struct mem_check {
  int ref_count;		/**< Number of allocations of this type. */
  MEM *next;			/**< Pointer to next in linked list. */
  char ref_name[BUFFER_LEN];	/**< Name of this allocation type. */
};

static MEM *my_check = NULL;

/*** WARNING! DO NOT USE strcasecoll IN THESE FUNCTIONS OR YOU'LL CREATE
 *** AN INFINITE LOOP. DANGER, WILL ROBINSON!
 ***/

/** Add an allocation check.
 * \param ref type of allocation.
 */
void
add_check(const char *ref)
{
  MEM *loop, *newcheck, *prev = NULL;
  size_t reflen;
  int cmp;

  if (!options.mem_check)
    return;

  for (loop = my_check; loop; loop = loop->next) {
    cmp = strcasecmp(ref, loop->ref_name);
    if (cmp == 0) {
      loop->ref_count++;
      return;
    } else if (cmp < 0)
      break;
    prev = loop;
  }
  reflen = strlen(ref) + 1;
  newcheck = (MEM *) malloc(sizeof(MEM) - BUFFER_LEN + reflen);
  memcpy(newcheck->ref_name, ref, reflen);
  newcheck->ref_count = 1;
  newcheck->next = loop;
  if (prev)
    prev->next = newcheck;
  else
    my_check = newcheck;
  return;
}

/** Remove an allocation check.
 * \param ref type of allocation to remove.
 */
void
del_check(const char *ref)
{
  MEM *loop, *prev = NULL;
  int cmp;

  if (!options.mem_check)
    return;

  for (loop = my_check; loop; loop = loop->next) {
    cmp = strcasecmp(ref, loop->ref_name);
    if (cmp == 0) {
      loop->ref_count--;
      if (!loop->ref_count) {
	if (!prev)
	  my_check = loop->next;
	else
	  prev->next = loop->next;
	free(loop);
      }
      return;
    } else if (cmp < 0)
      break;
    prev = loop;
  }
  do_rawlog(LT_CHECK,
	    T("ERROR: Tried deleting a check that was never added! :%s\n"),
	    ref);
}

/** List allocations.
 * \param player the enactor.
 */
void
list_mem_check(dbref player)
{
  MEM *loop;

  if (!options.mem_check)
    return;
  for (loop = my_check; loop; loop = loop->next) {
    notify_format(player, "%s : %d", loop->ref_name, loop->ref_count);
  }
}

/** Log all allocations.
 */
void
log_mem_check(void)
{
  MEM *loop;

  if (!options.mem_check)
    return;
  do_rawlog(LT_CHECK, "MEMCHECK dump starts");
  for (loop = my_check; loop; loop = loop->next) {
    do_rawlog(LT_CHECK, "%s : %d", loop->ref_name, loop->ref_count);
  }
  do_rawlog(LT_CHECK, "MEMCHECK dump ends");
}