/*
 Copyright (C) 1999-2004 IC & S  dbmail@ic-s.nl

 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/* $Id: debug.c 1862 2005-08-22 12:11:05Z paul $
 *
 * Debugging and memory checking functions */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "debug.h"
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>

struct debug_mem {
	long addr;
	int linenr;
	char fname[200];

	struct debug_mem *nextaddr;
};

typedef struct debug_mem debug_mem_t;

debug_mem_t *__dm_first = 0, *__dm_last = 0;

#define err_out_stream stderr
#define EXIT_CODE 75

/* the debug variables */
int TRACE_TO_SYSLOG = 1;	/* default: yes */
int TRACE_VERBOSE = 0;		/* default: no */
int TRACE_LEVEL = 2;		/* default: error operations */


/*
 * configure the debug settings
 */
void configure_debug(trace_t level, int trace_syslog, int trace_verbose)
{
	TRACE_LEVEL = level;
	TRACE_TO_SYSLOG = trace_syslog;
	TRACE_VERBOSE = trace_verbose;
}

void func_memtst(const char *filename, int line, int tst)
{
	if (tst != 0)
		trace(TRACE_FATAL,
		      "func_memtst(): fatal: %s:%d Memory error, result should not be NULL)",
		      filename, line);
}

#define TRACE_MESSAGE_SIZE 1024

void trace(trace_t level, char *formatstring, ...)
{
	/* 
	 * avoid malloc here so we can call trace from within signal 
	 * handlers on bsd too
	 */
	
	static char message[TRACE_MESSAGE_SIZE];
	va_list argp;

	memset(message, '\0', sizeof(message));
	
	va_start(argp, formatstring);
	vsnprintf(message, sizeof(message) - 1, formatstring, argp);
	va_end(argp);

	if (level <= TRACE_LEVEL) {
		if (TRACE_VERBOSE != 0) {
			fprintf(err_out_stream, "%s", message);
			if (message[strlen(message)] != '\n')
				fprintf(err_out_stream, "\n");
		}
		if (TRACE_TO_SYSLOG != 0) {
			if (message[strlen(message)] == '\n')
				message[strlen(message)] = '\0';
			if (level <= TRACE_WARNING) {
				/* set LOG_ALERT at warnings */
				syslog(LOG_ALERT, "%s", message);
			} else
				syslog(LOG_NOTICE, "%s", message);
		}
	}

	/* very big fatal error 
	 * bailout */

	if (level == TRACE_FATAL)
		exit(EX_TEMPFAIL);

	if (level == TRACE_STOP)
		exit(EX_TEMPFAIL);
}

void *__debug_malloc(unsigned long size, const char *fname, int linenr)
{
	void *ptr = malloc(size);
	debug_mem_t *new;

	if (!ptr)
		return NULL;

	new = (debug_mem_t *) malloc(sizeof(debug_mem_t));

	if (!new) {
		trace(TRACE_WARNING,
		      "__debug_malloc(): could not add malloc to list (call: %s, %d)\n",
		      fname, linenr);
		return ptr;
	}

	new->addr = (long) ptr;
	new->linenr = linenr;
	if (fname)
		strncpy(new->fname, fname, 200);
	else
		new->fname[0] = 0;

	new->fname[199] = 0;
	new->nextaddr = 0;

	if (!__dm_first) {
		__dm_first = new;
		__dm_last = new;
	} else {
		__dm_last->nextaddr = new;
		__dm_last = new;
	}

	return ptr;
}

void __debug_free(void *ptr, const char *fname, int linenr)
{
	debug_mem_t *curr = __dm_first, *prev = NULL;

	if (!ptr)
		return;

	while (curr && curr->addr != (long) ptr) {
		prev = curr;
		curr = curr->nextaddr;
	}

	if (!curr) {
		trace(TRACE_WARNING,
		      "__debug_free(): freeing a memory block that is not in the list\n");
		trace(TRACE_WARNING,
		      "__debug_free(): called in file %s, line %d\n",
		      fname, linenr);
		free(ptr);
		return;
	}

	if (prev) {
		prev->nextaddr = curr->nextaddr;
		if (__dm_last == curr)
			__dm_last = prev;
	} else {
		__dm_first = __dm_first->nextaddr;
		if (__dm_first == 0)
			__dm_last = 0;

/*      if (__dm_last == __dm_first)
	__dm_last = 0; */
	}

	free(curr);
	free(ptr);
}

void __debug_dumpallocs()
{
#ifdef __DEBUG_TRACE_MEMALLOC

	debug_mem_t *curr = __dm_first;

	trace(TRACE_WARNING,
	      "__debug_dumpallocs(): retrieving list of currently allocated items\n");

	while (curr) {
		trace(TRACE_WARNING, "    From %s, line %d: %X\n",
		      curr->fname, curr->linenr, curr);
		curr = curr->nextaddr;
	}

	trace(TRACE_WARNING, "\n__debug_dumpallocs(): end\n");

#endif
}


char * dm_strdup(const char *str)
{
	char *new_str;
	size_t length;
	
	if (str) {
		length = strlen(str) + 1;
		new_str = (char *)dm_malloc(length);
		memcpy(new_str,str,length);
	} else {
		new_str = NULL;
	}

	return new_str;
}


syntax highlighted by Code2HTML, v. 0.9.1