/*
   ** MEMWATCH.C
   ** Nonintrusive ANSI C memory leak / overwrite detection
   ** Copyright (C) 1992-99 Johan Lindh
   ** All rights reserved.
   ** Version 2.59
   **
   ** 920810 JLI   [1.00]
   ** 920830 JLI   [1.10 double-free detection]
   ** 920912 JLI   [1.15 mwPuts, mwGrab/Drop, mwLimit]
   ** 921022 JLI   [1.20 ASSERT and VERIFY]
   ** 921105 JLI   [1.30 C++ support and TRACE]
   ** 921116 JLI   [1.40 mwSetOutFunc]
   ** 930215 JLI   [1.50 modified ASSERT/VERIFY]
   ** 930327 JLI   [1.51 better auto-init & PC-lint support]
   ** 930506 JLI   [1.55 MemWatch class, improved C++ support]
   ** 930507 JLI   [1.60 mwTest & CHECK()]
   ** 930809 JLI   [1.65 Abort/Retry/Ignore]
   ** 930820 JLI   [1.70 data dump when unfreed]
   ** 931016 JLI   [1.72 modified C++ new/delete handling]
   ** 931108 JLI   [1.77 mwSetAssertAction() & some small changes]
   ** 940110 JLI   [1.80 no-mans-land alloc/checking]
   ** 940328 JLI   [2.00 version 2.0 rewrite]
   **              Improved NML (no-mans-land) support.
   **              Improved performance (especially for free()ing!).
   **              Support for 'read-only' buffers (checksums)
   **              ^^ NOTE: I never did this... maybe I should?
   **              FBI (free'd block info) tagged before freed blocks
   **              Exporting of the mwCounter variable
   **              mwBreakOut() localizes debugger support
   **              Allocation statistics (global, per-module, per-line)
   **              Self-repair ability with relinking
   ** 950913 JLI   [2.10 improved garbage handling]
   ** 951201 JLI   [2.11 improved auto-free in emergencies]
   ** 960125 JLI   [X.01 implemented auto-checking using mwAutoCheck()]
   ** 960514 JLI   [2.12 undefining of existing macros]
   ** 960515 JLI   [2.13 possibility to use default new() & delete()]
   ** 960516 JLI   [2.20 suppression of file flushing on unfreed msgs]
   ** 960516 JLI   [2.21 better support for using MEMWATCH with DLL's]
   ** 960710 JLI   [X.02 multiple logs and mwFlushNow()]
   ** 960801 JLI   [2.22 merged X.01 version with current]
   ** 960805 JLI   [2.30 mwIsXXXXAddr() to avoid unneeded GP's]
   ** 960805 JLI   [2.31 merged X.02 version with current]
   ** 961002 JLI   [2.32 support for realloc() + fixed STDERR bug]
   ** 961222 JLI   [2.40 added mwMark() & mwUnmark()]
   ** 970101 JLI   [2.41 added over/underflow checking after failed ASSERT/VERIFY]
   ** 970113 JLI   [2.42 added support for PC-Lint 7.00g]
   ** 970207 JLI   [2.43 added support for strdup()]
   ** 970209 JLI   [2.44 changed default filename to lowercase]
   ** 970405 JLI   [2.45 fixed bug related with atexit() and some C++ compilers]
   ** 970723 JLI   [2.46 added MW_ARI_NULLREAD flag]
   ** 970813 JLI   [2.47 stabilized marker handling]
   ** 980317 JLI   [2.48 ripped out C++ support; wasn't working good anyway]
   ** 980318 JLI   [2.50 improved self-repair facilities & SIGSEGV support]
   ** 980417 JLI        [2.51 more checks for invalid addresses]
   ** 980512 JLI        [2.52 moved MW_ARI_NULLREAD to occur before aborting]
   ** 990112 JLI        [2.53 added check for empty heap to mwIsOwned]
   ** 990217 JLI        [2.55 improved the emergency repairs diagnostics and NML]
   ** 990224 JLI        [2.56 changed ordering of members in structures]
   ** 990303 JLI        [2.57 first maybe-fixit-for-hpux test]
   ** 990516 JLI        [2.58 added 'static' to the definition of mwAutoInit]
   ** 990517 JLI        [2.59 fixed some high-sensitivity warnings]
 */

#define __MEMWATCH_CPP

#ifdef MW_NOCPP
#define MEMWATCH_NOCPP
#endif
#ifdef MW_STDIO
#define MEMWATCH_STDIO
#endif

/***********************************************************************
** Include files
***********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <time.h>
#include "memwatch.h"

#ifndef toupper
#include <ctype.h>
#endif

/***********************************************************************
** Defines & other weird stuff
***********************************************************************/

/*lint -save -e767 */
#define VERSION     "2.59"	/* the current version number */
#define CHKVAL(mw)  (0xFE0180L^(long)mw->count^(long)mw->size^(long)mw->line)
#define FLUSH()     mwFlush()
#define TESTS(f,l)  if(mwTestAlways) (void)mwTestNow(f,l,1)
#define PRECHK      0x01234567L
#define POSTCHK     0x76543210L
/*lint -restore */

#define MW_NML      0x0001

#ifdef _MSC_VER
#define COMMIT "c"		/* Microsoft C requires the 'c' to perform as desired */
#else
#define COMMIT ""		/* Normal ANSI */
#endif				/* _MSC_VER */

#ifdef __cplusplus
#define CPPTEXT "++"
#else
#define CPPTEXT ""
#endif				/* __cplusplus */

#ifdef MEMWATCH_STDIO
#define mwSTDERR stderr
#else
#define mwSTDERR mwLog
#endif

/***********************************************************************
** Defines to read/write 32 bit words in a portable way
** Note: Assumes that a 'long int' is 32 bits, and a 'char' is 8 bits.
***********************************************************************/

typedef unsigned char mwBYTE;
typedef unsigned long mwDWORD;

#define GETDWORD(l, cp) { \
	register mwBYTE *t_cp = (mwBYTE *)(cp); \
	(l) = ((mwDWORD)t_cp[0] << 24) \
	    | ((mwDWORD)t_cp[1] << 16) \
	    | ((mwDWORD)t_cp[2] << 8) \
	    | ((mwDWORD)t_cp[3]) \
	    ; \
}

#define PUTDWORD(l, cp) { \
	register mwDWORD t_l = (mwDWORD)(l); \
	register mwBYTE *t_cp = (mwBYTE *)(cp); \
	*t_cp++ = t_l >> 24; \
	*t_cp++ = t_l >> 16; \
	*t_cp++ = t_l >> 8; \
	*t_cp   = t_l; \
}

/***********************************************************************
** Typedefs & structures
***********************************************************************/

/* main data holding area, precedes actual allocation */
typedef struct mwData_ mwData;
struct mwData_ {
	mwData *prev;		/* previous allocation in chain */
	mwData *next;		/* next allocation in chain */
	const char *file;	/* file name where allocated */
	long count;		/* action count */
	long check;		/* integrity check value */
#if 0
	long crc;		/* data crc value */
#endif
	size_t size;		/* size of allocation */
	int line;		/* line number where allocated */
	unsigned flag;		/* flag word */
};

/* statistics structure */
typedef struct mwStat_ mwStat;
struct mwStat_ {
	mwStat *next;		/* next statistic buffer */
	const char *file;
	long total;		/* total bytes allocated */
	long num;		/* total number of allocations */
	long max;		/* max allocated at one time */
	long curr;		/* current allocations */
	int line;
};

/* grabbing structure, 1K in size */
typedef struct mwGrabData_ mwGrabData;
struct mwGrabData_ {
	mwGrabData *next;
	int type;
	char blob[1024 - sizeof(mwGrabData *) - sizeof(int)];
};

typedef struct mwMarker_ mwMarker;
struct mwMarker_ {
	void *host;
	char *text;
	mwMarker *next;
	int level;
};

/***********************************************************************
** Static variables
***********************************************************************/

static int mwInited = 0;
static int mwInfoWritten = 0;
static int mwUseAtexit = 0;
static FILE *mwLog = NULL;
static int mwFlushing = 0;
static int mwStatLevel = MW_STAT_DEFAULT;
static int mwNML = MW_NML_DEFAULT;
static int mwFBI = 0;
static long mwAllocLimit = 0L;
static int mwUseLimit = 0;

static long mwNumCurAlloc = 0L;
static mwData *mwHead = NULL;
static mwData *mwTail = NULL;

static void (*mwOutFunction) (int) = NULL;
static int (*mwAriFunction) (const char *) = NULL;
static int mwAriAction = MW_ARI_ABORT;

static char mwPrintBuf[MW_TRACE_BUFFER + 8];

static long mwCounter = 0L;
static long mwErrors = 0L;

static int mwTestFlags = 0;
static int mwTestAlways = 0;

static FILE *mwLogB1 = NULL;
static int mwFlushingB1 = 0;

static mwStat *mwStatList = NULL;
static long mwStatTotAlloc = 0L;
static long mwStatMaxAlloc = 0L;
static long mwStatNumAlloc = 0L;
static long mwStatCurAlloc = 0L;
static long mwNmlNumAlloc = 0L;
static long mwNmlCurAlloc = 0L;

static mwGrabData *mwGrabList = NULL;
static long mwGrabSize = 0L;

static void *mwLastFree[MW_FREE_LIST];
static const char *mwLFfile[MW_FREE_LIST];
static int mwLFline[MW_FREE_LIST];
static int mwLFcur = 0;

static mwMarker *mwFirstMark = NULL;

static FILE *mwLogB2 = NULL;
static int mwFlushingB2 = 0;

/***********************************************************************
** Static function declarations
***********************************************************************/

static void mwAutoInit(void);
static FILE *mwLogR(void);
static void mwLogW(FILE *);
static int mwFlushR(void);
static void mwFlushW(int);
static void mwFlush(void);
static void mwIncErr(void);
static void mwUnlink(mwData *, const char *file, int line);
static int mwRelink(mwData *, const char *file, int line);
static int mwIsHeapOK(mwData * mw);
static int mwIsOwned(mwData * mw, const char *file, int line);
static int mwTestBuf(mwData * mw, const char *file, int line);
static void mwDefaultOutFunc(int);
static void mwWrite(const char *format,...);
static void mwLogFile(const char *name);
static size_t mwFreeUp(size_t, int);
static const void *mwTestMem(const void *, unsigned, int);
static int mwStrCmpI(const char *s1, const char *s2);
static int mwTestNow(const char *file, int line, int always_invoked);
static void mwDropAll(void);
static const char *mwGrabType(int type);
static unsigned mwGrab_(unsigned kb, int type, int silent);
static unsigned mwDrop_(unsigned kb, int type, int silent);
static int mwARI(const char *text);
static void mwStatReport(void);
static mwStat *mwStatGet(const char *, int, int);
static void mwStatAlloc(size_t, const char *, int);
static void mwStatFree(size_t, const char *, int);

/***********************************************************************
** System functions
***********************************************************************/

void mwInit(void)
{
	time_t tid;

	if (mwInited++ > 0)
		return;

	/* start a log if none is running */
	if (mwLogR() == NULL)
		mwLogFile("memwatch.log");
	if (mwLogR() == NULL) {
		int i;
		char buf[32];
		/* oops, could not open it! */
		/* probably because it's already open */
		/* so we try some other names */
		for (i = 1; i < 100; i++) {
			sprintf(buf, "memwat%02d.log", i);
			mwLogFile(buf);
			if (mwLogR() != NULL)
				break;
		}
	}
	/* initialize the statistics */
	mwStatList = NULL;
	mwStatTotAlloc = 0L;
	mwStatCurAlloc = 0L;
	mwStatMaxAlloc = 0L;
	mwStatNumAlloc = 0L;
	mwNmlCurAlloc = 0L;
	mwNmlNumAlloc = 0L;

	/* write informational header if needed */
	if (!mwInfoWritten) {
		mwInfoWritten = 1;
		(void) time(&tid);
		mwWrite(
			       "\n============="
			       " MEMWATCH " VERSION " Copyright (C) 1992-1999 Johan Lindh "
			       "=============\n");
		mwWrite("\nStarted at %s\n", ctime(&tid));

/**************************************************************** Generic */
#ifdef mwNew
		mwWrite("C++ new/delete tracking enabled\n");
#endif				/* mwNew */
#ifdef __STDC__
		mwWrite("Compiled as standard ANSI C\n");
#endif				/* __STDC__ */
/**************************************************************** Generic */

/************************************************************ Microsoft C */
#ifdef _MSC_VER
		mwWrite("Compiled using Microsoft C" CPPTEXT
			" %d.%02d\n", _MSC_VER / 100, _MSC_VER % 100);
#endif				/* _MSC_VER */
/************************************************************ Microsoft C */

/************************************************************** Borland C */
#ifdef __BORLANDC__
		mwWrite("Compiled using Borland C"
#ifdef __cplusplus
			"++ %d.%01d\n", __BCPLUSPLUS__ / 0x100, (__BCPLUSPLUS__ % 0x100) / 0x10);
#else
			" %d.%01d\n", __BORLANDC__ / 0x100, (__BORLANDC__ % 0x100) / 0x10);
#endif				/* __cplusplus */
#endif				/* __BORLANDC__ */
/************************************************************** Borland C */

/************************************************************** Watcom C */
#ifdef __WATCOMC__
		mwWrite("Compiled using Watcom C %d.%02d ",
			__WATCOMC__ / 100, __WATCOMC__ % 100);
#ifdef __FLAT__
		mwWrite("(32-bit flat model)");
#endif				/* __FLAT__ */
		mwWrite("\n");
#endif				/* __WATCOMC__ */
/************************************************************** Watcom C */

		mwWrite("\n");
		FLUSH();
	}
	if (mwUseAtexit)
		(void) atexit(mwAbort);
	return;
}

void mwAbort(void)
{
	mwData *mw;
	mwMarker *mrk;
	char *data;
	time_t tid;
	int c, i, j;
	int errors;
	long chk;

	tid = time(NULL);
	mwWrite("\nStopped at %s\n", ctime(&tid));

	if (!mwInited)
		mwWrite("internal: mwAbort(): MEMWATCH not initialized!\n");

	/* release the grab list */
	mwDropAll();

	/* report mwMarked items */
	while (mwFirstMark) {
		mrk = mwFirstMark->next;
		mwWrite("mark: %p: %s\n", mwFirstMark->host, mwFirstMark->text);
		free(mwFirstMark->text);
		free(mwFirstMark);
		mwFirstMark = mrk;
		mwErrors++;
	}

	/* release all still allocated memory */
	errors = 0;
	while (mwHead != NULL && errors < 3) {
		if (!mwIsOwned(mwHead, __FILE__, __LINE__)) {
			if (errors < 3) {
				errors++;
				mwWrite("internal: NML/unfreed scan restarting\n");
				FLUSH();
				mwHead = mwHead;
				continue;
			}
			mwWrite("internal: NML/unfreed scan aborted, heap too damaged\n");
			FLUSH();
			break;
		}
		mwFlushW(0);
		if (!(mwHead->flag & MW_NML)) {
			mwErrors++;
			data = ((char *) (mwHead + 1));
			mwWrite("unfreed: <%ld> %s(%d), %ld bytes at %p ",
				mwHead->count, mwHead->file, mwHead->line, (long) mwHead->size, data + sizeof(long));
			GETDWORD(chk, data);
			if (chk != PRECHK) {
				mwWrite("[underflowed] ");
				FLUSH();
			}
			GETDWORD(chk, (data + sizeof(long) + mwHead->size));
			if (chk != POSTCHK) {
				mwWrite("[overflowed] ");
				FLUSH();
			}
			mwWrite(" \t{");
			j = 16;
			if (mwHead->size < 16)
				j = (int) mwHead->size;
			for (i = 0; i < 16; i++) {
				if (i < j)
					mwWrite("%02X ",
						(unsigned char) *(data + sizeof(long) + i));
				else
					mwWrite(".. ");
			}
			for (i = 0; i < j; i++) {
				c = *(data + sizeof(long) + i);
				if (c < 32 || c > 126)
					c = '.';
				mwWrite("%c", c);
			}
			mwWrite("}\n");
			mw = mwHead;
			mwUnlink(mw, __FILE__, __LINE__);
			free(mw);
		} else {
			data = ((char *) (mwHead + 1)) + sizeof(long);
			if (mwTestMem(data, mwHead->size, MW_VAL_NML)) {
				mwErrors++;
				mwWrite("wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
					mwHead->count, data + sizeof(long), mwHead->file, mwHead->line);
				FLUSH();
			}
			mwNmlNumAlloc--;
			mwNmlCurAlloc -= mwHead->size;
			mw = mwHead;
			mwUnlink(mw, __FILE__, __LINE__);
			free(mw);
		}
	}

	if (mwNmlNumAlloc)
		mwWrite("internal: NoMansLand block counter %ld, not zero\n", mwNmlNumAlloc);
	if (mwNmlCurAlloc)
		mwWrite("internal: NoMansLand byte counter %ld, not zero\n", mwNmlCurAlloc);

	/* report statistics */
	mwStatReport();
	FLUSH();

	mwInited = 0;
	mwHead = mwTail = NULL;
	if (mwErrors)
		fprintf(mwSTDERR, "MEMWATCH detected %ld anomalies\n", mwErrors);
	mwLogFile(NULL);
	mwErrors = 0;
}

void mwTerm(void)
{
	if (mwInited == 1) {
		mwAbort();
		return;
	}
	if (!mwInited)
		mwWrite("internal: mwTerm(): MEMWATCH has not been started!\n");
	else
		mwInited--;
}

void mwStatistics(int level)
{
	mwAutoInit();
	if (level < 0)
		level = 0;
	mwStatLevel = level;
}

void mwAutoCheck(int onoff)
{
	mwAutoInit();
	mwTestAlways = onoff;
	if (onoff)
		mwTestFlags = MW_TEST_ALL;
}

void mwSetOutFunc(void (*func) (int))
{
	mwAutoInit();
	mwOutFunction = func;
}

int mwTest(const char *file, int line, int items)
{
	mwAutoInit();
	mwTestFlags = items;
	return mwTestNow(file, line, 0);
}

void mwBreakOut(const char *cause)
{
	fprintf(mwSTDERR, "breakout: %s\n", cause);
	mwWrite("breakout: %s\n", cause);
	return;
}

/*
   ** 981217 JLI: is it possible that ->next is not always set?
 */
void *mwMark(void *p, const char *desc, const char *file, unsigned line)
{
	mwMarker *mrk;
	unsigned n, isnew;
	char *buf;
	int tot, oflow = 0;
	char wherebuf[128];

	mwAutoInit();
	TESTS(NULL, 0);

	if (desc == NULL)
		desc = "unknown";
	if (file == NULL)
		file = "unknown";

	tot = sprintf(wherebuf, "%.48s called from %s(%d)", desc, file, line);
	if (tot >= (int) sizeof(wherebuf)) {
		wherebuf[sizeof(wherebuf) - 1] = 0;
		oflow = 1;
	}
	if (p == NULL) {
		mwWrite("mark: %s(%d), no mark for NULL:'%s' may be set\n", file, line, desc);
		return p;
	}
	if (mwFirstMark != NULL && !mwIsReadAddr(mwFirstMark, sizeof(mwMarker))) {
		mwWrite("mark: %s(%d), mwFirstMark (%p) is trashed, can't mark for %s\n",
			file, line, mwFirstMark, desc);
		return p;
	}
	for (mrk = mwFirstMark; mrk; mrk = mrk->next) {
		if (mrk->next != NULL && !mwIsReadAddr(mrk->next, sizeof(mwMarker))) {
			mwWrite("mark: %s(%d), mark(%p)->next(%p) is trashed, can't mark for %s\n",
				file, line, mrk, mrk->next, desc);
			return p;
		}
		if (mrk->host == p)
			break;
	}

	if (mrk == NULL) {
		isnew = 1;
		mrk = (mwMarker *) malloc(sizeof(mwMarker));
		if (mrk == NULL) {
			mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc);
			return p;
		}
		mrk->next = NULL;
		n = 0;
	} else {
		isnew = 0;
		n = strlen(mrk->text);
	}

	n += strlen(wherebuf);
	buf = (char *) malloc(n + 3);
	if (buf == NULL) {
		if (isnew)
			free(mrk);
		mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc);
		return p;
	}
	if (isnew) {
		memcpy(buf, wherebuf, n + 1);
		mrk->next = mwFirstMark;
		mrk->host = p;
		mrk->text = buf;
		mrk->level = 1;
		mwFirstMark = mrk;
	} else {
		strcpy(buf, mrk->text);
		strcat(buf, ", ");
		strcat(buf, wherebuf);
		free(mrk->text);
		mrk->text = buf;
		mrk->level++;
	}

	if (oflow) {
		mwIncErr();
		mwTrace(" [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n");
	}
	return p;
}

void *mwUnmark(void *p, const char *file, unsigned line)
{
	mwMarker *mrk, *prv;
	mrk = mwFirstMark;
	prv = NULL;
	while (mrk) {
		if (mrk->host == p) {
			if (mrk->level < 2) {
				if (prv)
					prv->next = mrk->next;
				else
					mwFirstMark = mrk->next;
				free(mrk->text);
				free(mrk);
				return p;
			}
			mrk->level--;
			return p;
		}
		prv = mrk;
		mrk = mrk->next;
	}
	mwWrite("mark: %s(%d), no mark found for %p\n", file, line, p);
	return p;
}

/***********************************************************************
** Safe memory checkers
**
** Using ifdefs, implement the operating-system specific mechanism
** of identifying a piece of memory as legal to access with read
** and write priviliges. Default: return nonzero for non-NULL pointers.
***********************************************************************/

static char mwDummy(char c)
{
	return c;
}

#ifndef MW_SAFEADDR
#ifdef WIN32
#define MW_SAFEADDR
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
int mwIsReadAddr(const void *p, unsigned len)
{
	if (p == NULL)
		return 0;
	if (IsBadReadPtr(p, len))
		return 0;
	return 1;
}
int mwIsSafeAddr(void *p, unsigned len)
{
	/* NOTE: For some reason, under Win95 the IsBad... */
	/* can return false for invalid pointers. */
	if (p == NULL)
		return 0;
	if (IsBadReadPtr(p, len) || IsBadWritePtr(p, len))
		return 0;
	return 1;
}
#endif				/* WIN32 */
#endif				/* MW_SAFEADDR */

#ifndef MW_SAFEADDR
#ifdef SIGSEGV
#define MW_SAFEADDR

typedef void (*mwSignalHandlerPtr) (int);
mwSignalHandlerPtr mwOldSIGSEGV = (mwSignalHandlerPtr) 0;
jmp_buf mwSIGSEGVjump;
static void mwSIGSEGV(int n);

static void mwSIGSEGV(int n)
{
	longjmp(mwSIGSEGVjump, 1);
}

int mwIsReadAddr(const void *p, unsigned len)
{
	const char *ptr;

	if (p == NULL)
		return 0;
	if (!len)
		return 1;

	/* set up to catch the SIGSEGV signal */
	mwOldSIGSEGV = signal(SIGSEGV, mwSIGSEGV);

	if (setjmp(mwSIGSEGVjump)) {
		signal(SIGSEGV, mwOldSIGSEGV);
		return 0;
	}
	/* read all the bytes in the range */
	ptr = (const char *) p;
	ptr += len;

	/* the reason for this rather strange construct is that */
	/* we want to keep the number of used parameters and locals */
	/* to a minimum. if we use len for a counter gcc will complain */
	/* it may get clobbered by longjmp() at high warning levels. */
	/* it's a harmless warning, but this way we don't have to see it. */
	do {
		ptr--;
		if (*ptr == 0x7C)
			(void) mwDummy((char) 0);
	} while (ptr != p);

	/* remove the handler */
	signal(SIGSEGV, mwOldSIGSEGV);

	return 1;
}
int mwIsSafeAddr(void *p, unsigned len)
{
	char *ptr;

	if (p == NULL)
		return 0;
	if (!len)
		return 1;

	/* set up to catch the SIGSEGV signal */
	mwOldSIGSEGV = signal(SIGSEGV, mwSIGSEGV);

	if (setjmp(mwSIGSEGVjump)) {
		signal(SIGSEGV, mwOldSIGSEGV);
		return 0;
	}
	/* read and write-back all the bytes in the range */
	ptr = (char *) p;
	ptr += len;

	/* the reason for this rather strange construct is that */
	/* we want to keep the number of used parameters and locals */
	/* to a minimum. if we use len for a counter gcc will complain */
	/* it may get clobbered by longjmp() at high warning levels. */
	/* it's a harmless warning, but this way we don't have to see it. */
	do {
		ptr--;
		*ptr = mwDummy(*ptr);
	} while (ptr != p);

	/* remove the handler */
	signal(SIGSEGV, mwOldSIGSEGV);

	return 1;
}
#endif				/* SIGSEGV */
#endif				/* MW_SAFEADDR */

#ifndef MW_SAFEADDR
int mwIsReadAddr(const void *p, unsigned len)
{
	if (p == NULL)
		return 0;
	if (len == 0)
		return 1;
	return 1;
}
int mwIsSafeAddr(void *p, unsigned len)
{
	if (p == NULL)
		return 0;
	if (len == 0)
		return 1;
	return 1;
}
#endif

/***********************************************************************
** Abort/Retry/Ignore handlers
***********************************************************************/

static int mwARI(const char *estr)
{
	char inbuf[81];
	int c;
	fprintf(mwSTDERR, "\n%s\nMEMWATCH: Abort, Retry or Ignore? ", estr);
	(void) fgets(inbuf, sizeof(inbuf), stdin);
	for (c = 0; inbuf[c] && inbuf[c] <= ' '; c++);
	c = inbuf[c];
	if (c == 'R' || c == 'r') {
		mwBreakOut(estr);
		return MW_ARI_RETRY;
	}
	if (c == 'I' || c == 'i')
		return MW_ARI_IGNORE;
	return MW_ARI_ABORT;
}

/* standard ARI handler (exported) */
int mwAriHandler(const char *estr)
{
	mwAutoInit();
	return mwARI(estr);
}

/* used to set the ARI function */
void mwSetAriFunc(int (*func) (const char *))
{
	mwAutoInit();
	mwAriFunction = func;
}

/***********************************************************************
** Allocation handlers
***********************************************************************/

void *mwMalloc(size_t size, const char *file, int line)
{
	size_t needed;
	mwData *mw;
	char *ptr;
	void *p;

	mwAutoInit();

	TESTS(file, line);

	mwCounter++;
	needed = sizeof(mwData) + sizeof(long) + sizeof(long) + size;

	/* if this allocation would violate the limit, fail it */
	if (mwUseLimit && ((long) size + mwStatCurAlloc > mwAllocLimit)) {
		mwWrite("limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
			mwCounter, file, line, (long) size, mwAllocLimit - mwStatCurAlloc);
		mwIncErr();
		FLUSH();
		return NULL;
	}
	mw = (mwData *) malloc(needed);
	if (mw == NULL) {
		if (mwFreeUp(needed, 0) >= needed) {
			mw = (mwData *) malloc(needed);
			if (mw == NULL) {
				mwWrite("internal: mwFreeUp(%u) reported success, but malloc() fails\n", needed);
				mwIncErr();
				FLUSH();
			}
		}
		if (mw == NULL) {
			mwWrite("fail: <%ld> %s(%d), %ld wanted %ld allocated\n",
				mwCounter, file, line, (long) size, mwStatCurAlloc);
			mwIncErr();
			FLUSH();
			return NULL;
		}
	}
	mw->count = mwCounter;
	mw->prev = NULL;
	mw->next = mwHead;
	mw->file = file;
	mw->size = size;
	mw->line = line;
	mw->flag = 0;
	mw->check = CHKVAL(mw);

	if (mwHead)
		mwHead->prev = mw;
	mwHead = mw;
	if (mwTail == NULL)
		mwTail = mw;

	ptr = (char *) (void *) (mw + 1);
	PUTDWORD(PRECHK, ptr);	/* '*(long*)ptr = PRECHK;' */
	ptr += sizeof(long);
	p = ptr;
	memset(ptr, MW_VAL_NEW, size);
	ptr += size;
	PUTDWORD(POSTCHK, ptr);	/* '*(long*)ptr = POSTCHK;' */

	mwNumCurAlloc++;
	mwStatCurAlloc += (long) size;
	mwStatTotAlloc += (long) size;
	if (mwStatCurAlloc > mwStatMaxAlloc)
		mwStatMaxAlloc = mwStatCurAlloc;
	mwStatNumAlloc++;

	if (mwStatLevel)
		mwStatAlloc(size, file, line);

	return p;
}

void *mwRealloc(void *p, size_t size, const char *file, int line)
{
	int oldUseLimit, i;
	mwData *mw;
	char *ptr;

	mwAutoInit();

	if (p == NULL)
		return mwMalloc(size, file, line);
	if (size == 0) {
		mwFree(p, file, line);
		return NULL;
	}
	/* do the quick ownership test */
	mw = (mwData *) (((char *) p) - sizeof(long) - sizeof(mwData));
	if (mwIsOwned(mw, file, line)) {

		/* if the buffer is an NML, treat this as a double-free */
		if (mw->flag & MW_NML) {
			mwIncErr();
			if (*((unsigned char *) (mw + 1) + sizeof(long)) != MW_VAL_NML) {
				mwWrite("internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
					mwCounter, file, line, mw);
			}
			goto check_dbl_free;
		}
		/* if this allocation would violate the limit, fail it */
		if (mwUseLimit && ((long) size + mwStatCurAlloc - (long) mw->size > mwAllocLimit)) {
			TESTS(file, line);
			mwCounter++;
			mwWrite("limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
				mwCounter, file, line, (unsigned long) size - mw->size, mwAllocLimit - mwStatCurAlloc);
			mwIncErr();
			FLUSH();
			return NULL;
		}
		/* fake realloc operation */
		oldUseLimit = mwUseLimit;
		mwUseLimit = 0;
		ptr = (char *) mwMalloc(size, file, line);
		if (ptr != NULL) {
			if (size < mw->size)
				memcpy(ptr, p, size);
			else
				memcpy(ptr, p, mw->size);
			mwFree(p, file, line);
		}
		mwUseLimit = oldUseLimit;
		return (void *) ptr;
	}
	/* Unknown pointer! */

	/* using free'd pointer? */
      check_dbl_free:
	for (i = 0; i < MW_FREE_LIST; i++) {
		if (mwLastFree[i] == p) {
			mwIncErr();
			mwWrite("realloc: <%ld> %s(%d), %p was"
				" freed from %s(%d)\n",
				mwCounter, file, line, p,
				mwLFfile[i], mwLFline[i]);
			FLUSH();
			return NULL;
		}
	}

	/* some weird pointer */
	mwIncErr();
	mwWrite("realloc: <%ld> %s(%d), unknown pointer %p\n",
		mwCounter, file, line, p);
	FLUSH();
	return NULL;
}

char *mwStrdup(char *str, const char *file, int line)
{
	size_t len;
	char *newstring;
	if (str == NULL) {
		mwIncErr();
		mwWrite("strdup: <%ld> %s(%d), strdup(NULL) called\n",
			mwCounter, file, line);
		FLUSH();
		return NULL;
	}
	len = strlen(str) + 1;
	newstring = (char *) mwMalloc(len, file, line);
	if (newstring != NULL)
		memcpy(newstring, str, len);
	return newstring;
}

void mwFree(void *p, const char *file, int line)
{
	int i;
	mwData *mw;
	char buffer[sizeof(mwData) + sizeof(long) + 64];

	TESTS(file, line);

	/* this code is in support of C++ delete */
	if (file == NULL) {
		mwFree_(p);
		return;
	}
	mwAutoInit();
	mwCounter++;

	/* on NULL free, write a warning and return */
	if (p == NULL) {
		mwWrite("NULL free: <%ld> %s(%d), NULL pointer free'd\n",
			mwCounter, file, line);
		FLUSH();
		return;
	}
	/* do the quick ownership test */
	mw = (mwData *) (((char *) p) - sizeof(long) - sizeof(mwData));

	if (mwIsOwned(mw, file, line)) {
		(void) mwTestBuf(mw, file, line);

		/* if the buffer is an NML, treat this as a double-free */
		if (mw->flag & MW_NML) {
			if (*((unsigned char *) (mw + 1) + sizeof(long)) != MW_VAL_NML) {
				mwWrite("internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
					mwCounter, file, line, mw);
			}
			goto check_dbl_free;
		}
		/* update the statistics */
		mwNumCurAlloc--;
		mwStatCurAlloc -= (long) mw->size;
		if (mwStatLevel)
			mwStatFree(mw->size, mw->file, mw->line);

		/* we should either free the allocation or keep it as NML */
		if (mwNML) {
			mw->flag |= MW_NML;
			mwNmlNumAlloc++;
			mwNmlCurAlloc += (long) mw->size;
			memset((char *) (mw + 1) + sizeof(long), MW_VAL_NML, mw->size);
		} else {
			/* unlink the allocation, and enter the post-free data */
			mwUnlink(mw, file, line);
			memset(mw, MW_VAL_DEL,
			       mw->size + sizeof(mwData) + sizeof(long) + sizeof(long));
			if (mwFBI) {
				memset(mw, '.', sizeof(mwData) + sizeof(long));
				sprintf(buffer, "FBI<%ld>%s(%d)", mwCounter, file, line);
				strncpy((char *) (void *) mw, buffer, sizeof(mwData) + sizeof(long));
			}
			free(mw);
		}

		/* add the pointer to the last-free track */
		mwLFfile[mwLFcur] = file;
		mwLFline[mwLFcur] = line;
		mwLastFree[mwLFcur++] = p;
		if (mwLFcur == MW_FREE_LIST)
			mwLFcur = 0;

		return;
	}
	/* check for double-freeing */
      check_dbl_free:
	for (i = 0; i < MW_FREE_LIST; i++) {
		if (mwLastFree[i] == p) {
			mwIncErr();
			mwWrite("double-free: <%ld> %s(%d), %p was"
				" freed from %s(%d)\n",
				mwCounter, file, line, p,
				mwLFfile[i], mwLFline[i]);
			FLUSH();
			return;
		}
	}

	/* some weird pointer... block the free */
	mwIncErr();
	mwWrite("WILD free: <%ld> %s(%d), unknown pointer %p\n",
		mwCounter, file, line, p);
	FLUSH();
	return;
}

void *mwCalloc(size_t a, size_t b, const char *file, int line)
{
	void *p;
	size_t size = a * b;
	p = mwMalloc(size, file, line);
	if (p == NULL)
		return NULL;
	memset(p, 0, size);
	return p;
}

void mwFree_(void *p)
{
	TESTS(NULL, 0);
	free(p);
}

void *mwMalloc_(size_t size)
{
	TESTS(NULL, 0);
	return malloc(size);
}

void *mwRealloc_(void *p, size_t size)
{
	TESTS(NULL, 0);
	return realloc(p, size);
}

void *mwCalloc_(size_t a, size_t b)
{
	TESTS(NULL, 0);
	return calloc(a, b);
}

void mwFlushNow(void)
{
	if (mwLogR())
		fflush(mwLogR());
	return;
}

void mwDoFlush(int onoff)
{
	mwFlushW(onoff < 1 ? 0 : onoff);
	if (onoff)
		if (mwLogR())
			fflush(mwLogR());
	return;
}

void mwLimit(long lim)
{
	TESTS(NULL, 0);
	mwWrite("limit: old limit = ");
	if (!mwAllocLimit)
		mwWrite("none");
	else
		mwWrite("%ld bytes", mwAllocLimit);
	mwWrite(", new limit = ");
	if (!lim) {
		mwWrite("none\n");
		mwUseLimit = 0;
	} else {
		mwWrite("%ld bytes\n", lim);
		mwUseLimit = 1;
	}
	mwAllocLimit = lim;
	FLUSH();
}

void mwSetAriAction(int action)
{
	TESTS(NULL, 0);
	mwAriAction = action;
	return;
}

int mwAssert(int exp, const char *exps, const char *fn, int ln)
{
	int i;
	char buffer[MW_TRACE_BUFFER + 8];
	TESTS(fn, ln);
	if (exp)
		return 0;
	mwAutoInit();
	mwIncErr();
	mwCounter++;
	mwWrite("assert trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps);
	if (mwAriFunction != NULL) {
		sprintf(buffer, "MEMWATCH: assert trap: %s(%d), %s", fn, ln, exps);
		i = (*mwAriFunction) (buffer);
		switch (i) {
		case MW_ARI_IGNORE:
			mwWrite("assert trap: <%ld> IGNORED - execution continues\n", mwCounter);
			return 0;
		case MW_ARI_RETRY:
			mwWrite("assert trap: <%ld> RETRY - executing again\n", mwCounter);
			return 1;
		}
	} else {
		if (mwAriAction & MW_ARI_IGNORE) {
			mwWrite("assert trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter);
			return 0;
		}
		fprintf(mwSTDERR, "\nMEMWATCH: assert trap: %s(%d), %s\n", fn, ln, exps);
	}

	FLUSH();
	(void) mwTestNow(fn, ln, 1);
	FLUSH();

	if (mwAriAction & MW_ARI_NULLREAD) {
		/* This is made in an attempt to kick in */
		/* any debuggers or OS stack traces */
		FLUSH();
		/*lint -save -e413 */
		i = *((int *) NULL);
		mwDummy((char) i);
		/*lint -restore */
	}
	exit(255);
	/* NOT REACHED - the return statement is in to keep */
	/* stupid compilers from squeaking about differing return modes. */
	/* Smart compilers instead say 'code unreachable...' */
	/*lint -save -e527 */
	return 0;
	/*lint -restore */
}

int mwVerify(int exp, const char *exps, const char *fn, int ln)
{
	int i;
	char buffer[MW_TRACE_BUFFER + 8];
	TESTS(fn, ln);
	if (exp)
		return 0;
	mwAutoInit();
	mwIncErr();
	mwCounter++;
	mwWrite("verify trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps);
	if (mwAriFunction != NULL) {
		sprintf(buffer, "MEMWATCH: verify trap: %s(%d), %s", fn, ln, exps);
		i = (*mwAriFunction) (buffer);
		if (i == 0) {
			mwWrite("verify trap: <%ld> IGNORED - execution continues\n", mwCounter);
			return 0;
		}
		if (i == 1) {
			mwWrite("verify trap: <%ld> RETRY - executing again\n", mwCounter);
			return 1;
		}
	} else {
		if (mwAriAction & MW_ARI_NULLREAD) {
			/* This is made in an attempt to kick in */
			/* any debuggers or OS stack traces */
			FLUSH();
			/*lint -save -e413 */
			i = *((int *) NULL);
			mwDummy((char) i);
			/*lint -restore */
		}
		if (mwAriAction & MW_ARI_IGNORE) {
			mwWrite("verify trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter);
			return 0;
		}
		fprintf(mwSTDERR, "\nMEMWATCH: verify trap: %s(%d), %s\n", fn, ln, exps);
	}
	FLUSH();
	(void) mwTestNow(fn, ln, 1);
	FLUSH();
	exit(255);
	/* NOT REACHED - the return statement is in to keep */
	/* stupid compilers from squeaking about differing return modes. */
	/* Smart compilers instead say 'code unreachable...' */
	/*lint -save -e527 */
	return 0;
	/*lint -restore */
}

void mwTrace(const char *format,...)
{
	int tot, oflow = 0;
	va_list mark;

	mwAutoInit();
	TESTS(NULL, 0);
	if (mwOutFunction == NULL)
		mwOutFunction = mwDefaultOutFunc;

	va_start(mark, format);
	tot = vsprintf(mwPrintBuf, format, mark);
	va_end(mark);
	if (tot >= MW_TRACE_BUFFER) {
		mwPrintBuf[MW_TRACE_BUFFER] = 0;
		oflow = 1;
	}
	for (tot = 0; mwPrintBuf[tot]; tot++)
		(*mwOutFunction) (mwPrintBuf[tot]);
	if (oflow) {
		mwIncErr();
		mwTrace(" [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n");
	}
	FLUSH();
}


/***********************************************************************
** Grab & Drop
***********************************************************************/

unsigned mwGrab(unsigned kb)
{
	TESTS(NULL, 0);
	return mwGrab_(kb, MW_VAL_GRB, 0);
}

unsigned mwDrop(unsigned kb)
{
	TESTS(NULL, 0);
	return mwDrop_(kb, MW_VAL_GRB, 0);
}

static void mwDropAll()
{
	TESTS(NULL, 0);
	(void) mwDrop_(0, MW_VAL_GRB, 0);
	(void) mwDrop_(0, MW_VAL_NML, 0);
	if (mwGrabList != NULL)
		mwWrite("internal: the grab list is not empty after mwDropAll()\n");
}

static const char *mwGrabType(int type)
{
	switch (type) {
	case MW_VAL_GRB:
		return "grabbed";
	case MW_VAL_NML:
		return "no-mans-land";
	default:
		/* do nothing */
		;
	}
	return "<unknown type>";
}

static unsigned mwGrab_(unsigned kb, int type, int silent)
{
	unsigned i = kb;
	mwGrabData *gd;
	if (!kb)
		i = kb = 65000U;

	for (; kb; kb--) {
		if (mwUseLimit &&
		    (mwStatCurAlloc + mwGrabSize + (long) sizeof(mwGrabData) > mwAllocLimit)) {
			if (!silent) {
				mwWrite("grabbed: all allowed memory to %s (%u kb)\n",
					mwGrabType(type), i - kb);
				FLUSH();
			}
			return i - kb;
		}
		gd = (mwGrabData *) malloc(sizeof(mwGrabData));
		if (gd == NULL) {
			if (!silent) {
				mwWrite("grabbed: all available memory to %s (%u kb)\n",
					mwGrabType(type), i - kb);
				FLUSH();
			}
			return i - kb;
		}
		mwGrabSize += (long) sizeof(mwGrabData);
		gd->next = mwGrabList;
		memset(gd->blob, type, sizeof(gd->blob));
		gd->type = type;
		mwGrabList = gd;
	}
	if (!silent) {
		mwWrite("grabbed: %u kilobytes of %s memory\n", i, mwGrabType(type));
		FLUSH();
	}
	return i;
}

static unsigned mwDrop_(unsigned kb, int type, int silent)
{
	unsigned i = kb;
	mwGrabData *gd, *tmp, *pr;
	const void *p;

	if (mwGrabList == NULL && kb == 0)
		return 0;
	if (!kb)
		i = kb = 60000U;

	pr = NULL;
	gd = mwGrabList;
	for (; kb;) {
		if (gd == NULL) {
			if (i - kb > 0 && !silent) {
				mwWrite("dropped: all %s memory (%u kb)\n", mwGrabType(type), i - kb);
				FLUSH();
			}
			return i - kb;
		}
		if (gd->type == type) {
			if (pr)
				pr->next = gd->next;
			kb--;
			tmp = gd;
			if (mwGrabList == gd)
				mwGrabList = gd->next;
			gd = gd->next;
			p = mwTestMem(tmp->blob, sizeof(tmp->blob), type);
			if (p != NULL) {
				mwWrite("wild pointer: <%ld> %s memory hit at %p\n",
					mwCounter, mwGrabType(type), p);
				FLUSH();
			}
			mwGrabSize -= (long) sizeof(mwGrabData);
			free(tmp);
		} else {
			pr = gd;
			gd = gd->next;
		}
	}
	if (!silent) {
		mwWrite("dropped: %u kilobytes of %s memory\n", i, mwGrabType(type));
		FLUSH();
	}
	return i;
}

/***********************************************************************
** No-Mans-Land
***********************************************************************/

void mwNoMansLand(int level)
{
	mwAutoInit();
	TESTS(NULL, 0);
	switch (level) {
	case MW_NML_NONE:
		(void) mwDrop_(0, MW_VAL_NML, 0);
		break;
	case MW_NML_FREE:
		break;
	case MW_NML_ALL:
		(void) mwGrab_(0, MW_VAL_NML, 0);
		break;
	default:
		return;
	}
	mwNML = level;
}

/***********************************************************************
** Static functions
***********************************************************************/

static void mwAutoInit(void)
{
	if (mwInited)
		return;
	mwUseAtexit = 1;
	mwInit();
	return;
}

static FILE *mwLogR()
{
	if ((mwLog == mwLogB1) && (mwLog == mwLogB2))
		return mwLog;
	if (mwLog == mwLogB1)
		mwLogB2 = mwLog;
	if (mwLog == mwLogB2)
		mwLogB1 = mwLog;
	if (mwLogB1 == mwLogB2)
		mwLog = mwLogB1;
	if ((mwLog == mwLogB1) && (mwLog == mwLogB2)) {
		mwWrite("internal: log file handle damaged and recovered\n");
		FLUSH();
		return mwLog;
	}
	fprintf(mwSTDERR, "\nMEMWATCH: log file handle destroyed, using mwSTDERR\n");
	mwLog = mwLogB1 = mwLogB2 = mwSTDERR;
	return mwSTDERR;
}

static void mwLogW(FILE * p)
{
	mwLog = mwLogB1 = mwLogB2 = p;
}

static int mwFlushR()
{
	if ((mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2))
		return mwFlushing;
	if (mwFlushing == mwFlushingB1)
		mwFlushingB2 = mwFlushing;
	if (mwFlushing == mwFlushingB2)
		mwFlushingB1 = mwFlushing;
	if (mwFlushingB1 == mwFlushingB2)
		mwFlushing = mwFlushingB1;
	if ((mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2)) {
		mwWrite("internal: flushing flag damaged and recovered\n");
		FLUSH();
		return mwFlushing;
	}
	mwWrite("internal: flushing flag destroyed, so set to true\n");
	mwFlushing = mwFlushingB1 = mwFlushingB2 = 1;
	return 1;
}

static void mwFlushW(int n)
{
	mwFlushing = mwFlushingB1 = mwFlushingB2 = n;
}

static void mwIncErr()
{
	mwErrors++;
	mwFlushW(mwFlushR() + 1);
	FLUSH();
}

static void mwFlush()
{
	if (mwLogR() == NULL)
		return;
#ifdef MW_FLUSH
	fflush(mwLogR());
#else
	if (mwFlushR())
		fflush(mwLogR());
#endif
	return;
}

static void mwUnlink(mwData * mw, const char *file, int line)
{
	if (mw->prev == NULL) {
		if (mwHead != mw)
			mwWrite("internal: <%ld> %s(%d), MW-%p: link1 NULL, but not head\n",
				mwCounter, file, line, mw);
		mwHead = mw->next;
	} else {
		if (mw->prev->next != mw)
			mwWrite("internal: <%ld> %s(%d), MW-%p: link1 failure\n",
				mwCounter, file, line, mw);
		else
			mw->prev->next = mw->next;
	}
	if (mw->next == NULL) {
		if (mwTail != mw)
			mwWrite("internal: <%ld> %s(%d), MW-%p: link2 NULL, but not tail\n",
				mwCounter, file, line, mw);
		mwTail = mw->prev;
	} else {
		if (mw->next->prev != mw)
			mwWrite("internal: <%ld> %s(%d), MW-%p: link2 failure\n",
				mwCounter, file, line, mw);
		else
			mw->next->prev = mw->prev;
	}
}

/*
   ** Relinking tries to repair a damaged mw block.
   ** Returns nonzero if it thinks it successfully
   ** repaired the heap chain.
 */
static int mwRelink(mwData * mw, const char *file, int line)
{
	int fails;
	mwData *mw1, *mw2;
	long count, size;
	mwStat *ms;

	if (file == NULL)
		file = "unknown";

	if (mw == NULL) {
		mwWrite("relink: cannot repair MW at NULL\n");
		FLUSH();
		goto emergency;
	}
	if (!mwIsSafeAddr(mw, sizeof(mwData))) {
		mwWrite("relink: MW-%p is a garbage pointer\n");
		FLUSH();
		goto emergency;
	}
	mwWrite("relink: <%ld> %s(%d) attempting to repair MW-%p...\n", mwCounter, file, line, mw);
	FLUSH();
	fails = 0;

	/* Repair from head */
	if (mwHead != mw) {
		if (!mwIsSafeAddr(mwHead, sizeof(mwData))) {
			mwWrite("relink: failed for MW-%p; head pointer destroyed\n", mw);
			FLUSH();
			goto emergency;
		}
		for (mw1 = mwHead; mw1; mw1 = mw1->next) {
			if (mw1->next == mw) {
				mw->prev = mw1;
				break;
			}
			if (mw1->next &&
			    (!mwIsSafeAddr(mw1->next, sizeof(mwData)) || mw1->next->prev != mw1)) {
				mwWrite("relink: failed for MW-%p; forward chain fragmented at MW-%p: 'next' is %p\n", mw, mw1, mw1->next);
				FLUSH();
				goto emergency;
			}
		}
		if (mw1 == NULL) {
			mwWrite("relink: MW-%p not found in forward chain search\n", mw);
			FLUSH();
			fails++;
		}
	} else {
		mwWrite("relink: MW-%p is the head (first) allocation\n", mw);
		if (mw->prev != NULL) {
			mwWrite("relink: MW-%p prev pointer is non-NULL, you have a wild pointer\n", mw);
			mw->prev = NULL;
		}
	}

	/* Repair from tail */
	if (mwTail != mw) {
		if (!mwIsSafeAddr(mwTail, sizeof(mwData))) {
			mwWrite("relink: failed for MW-%p; tail pointer destroyed\n", mw);
			FLUSH();
			goto emergency;
		}
		for (mw1 = mwTail; mw1; mw1 = mw1->prev) {
			if (mw1->prev == mw) {
				mw->next = mw1;
				break;
			}
			if (mw1->prev && (!mwIsSafeAddr(mw1->prev, sizeof(mwData)) || mw1->prev->next != mw1)) {
				mwWrite("relink: failed for MW-%p; reverse chain fragmented at MW-%p, 'prev' is %p\n", mw, mw1, mw1->prev);
				FLUSH();
				goto emergency;
			}
		}
		if (mw1 == NULL) {
			mwWrite("relink: MW-%p not found in reverse chain search\n", mw);
			FLUSH();
			fails++;
		}
	} else {
		mwWrite("relink: MW-%p is the tail (last) allocation\n", mw);
		if (mw->next != NULL) {
			mwWrite("relink: MW-%p next pointer is non-NULL, you have a wild pointer\n", mw);
			mw->next = NULL;
		}
	}

	if (fails > 1) {
		mwWrite("relink: heap appears intact, MW-%p probably garbage pointer\n", mw);
		FLUSH();
		goto verifyok;
	}
	/* restore MW info where possible */
	if (mwIsReadAddr(mw->file, 1)) {
		ms = mwStatGet(mw->file, -1, 0);
		if (ms == NULL)
			mw->file = "<relinked>";
	}
	mw->check = CHKVAL(mw);
	goto verifyok;

	/* Emergency repair */
      emergency:

	if (mwHead == NULL && mwTail == NULL) {
		if (mwStatCurAlloc == 0)
			mwWrite("relink: <%ld> %s(%d) heap is empty, nothing to repair\n", mwCounter, file, line);
		else
			mwWrite("relink: <%ld> %s(%d) heap damaged beyond repair\n", mwCounter, file, line);
		FLUSH();
		return 0;
	}
	mwWrite("relink: <%ld> %s(%d) attempting emergency repairs...\n", mwCounter, file, line);
	FLUSH();

	if (mwHead == NULL || mwTail == NULL) {
		if (mwHead == NULL)
			mwWrite("relink: mwHead is NULL, but mwTail is %p\n", mwTail);
		else
			mwWrite("relink: mwTail is NULL, but mwHead is %p\n", mwHead);
	}
	mw1 = NULL;
	if (mwHead != NULL) {
		if (!mwIsReadAddr(mwHead, sizeof(mwData)) || mwHead->check != CHKVAL(mwHead)) {
			mwWrite("relink: mwHead (MW-%p) is damaged, skipping forward scan\n", mwHead);
			mwHead = NULL;
			goto scan_reverse;
		}
		if (mwHead->prev != NULL) {
			mwWrite("relink: the mwHead pointer's 'prev' member is %p, not NULL\n", mwHead->prev);
		}
		for (mw1 = mwHead; mw1; mw1 = mw1->next) {
			if (mw1->next) {
				if (!mwIsReadAddr(mw1->next, sizeof(mwData)) ||
				    !mw1->next->check != CHKVAL(mw1) ||
				    mw1->next->prev != mw1) {
					mwWrite("relink: forward chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n",
						mw1, mw1->size, (mw->flag & MW_NML) ? "NoMansLand " : "", mw1->file, mw1->line);
					if (mwIsReadAddr(mw1->next, sizeof(mwData))) {
						mwWrite("relink: forward chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n",
							mw1->next, mw1->size, (mw->flag & MW_NML) ? "NoMansLand " : "",
							mwIsReadAddr(mw1->file, 16) ? mw1->file : "<garbage-pointer>", mw1->line);
					} else {
						mwWrite("relink: the 'next' pointer of this MW points to %p, which is out-of-legal-access\n",
							mw1->next);
					}
					break;
				}
			}
		}
	}
      scan_reverse:
	mw2 = NULL;
	if (mwTail != NULL) {
		if (!mwIsReadAddr(mwTail, sizeof(mwData)) || mwTail->check != CHKVAL(mwTail)) {
			mwWrite("relink: mwTail (%p) is damaged, skipping reverse scan\n", mwTail);
			mwTail = NULL;
			goto analyze;
		}
		if (mwTail->next != NULL) {
			mwWrite("relink: the mwTail pointer's 'next' member is %p, not NULL\n", mwTail->next);
		}
		for (mw2 = mwTail; mw2; mw2 = mw2->prev) {
			if (mw2->prev) {
				if (!mwIsReadAddr(mw2->prev, sizeof(mwData)) ||
				    !mw2->prev->check != CHKVAL(mw2) ||
				    mw2->prev->next != mw2) {
					mwWrite("relink: reverse chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n",
						mw2, mw2->size, (mw->flag & MW_NML) ? "NoMansLand " : "", mw2->file, mw2->line);
					if (mwIsReadAddr(mw2->prev, sizeof(mwData))) {
						mwWrite("relink: reverse chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n",
							mw2->prev, mw2->size, (mw->flag & MW_NML) ? "NoMansLand " : "",
							mwIsReadAddr(mw2->file, 16) ? mw2->file : "<garbage-pointer>", mw2->line);
					} else {
						mwWrite("relink: the 'prev' pointer of this MW points to %p, which is out-of-legal-access\n",
							mw2->prev);
					}
					break;
				}
			}
		}
	}
      analyze:
	if (mwHead == NULL && mwTail == NULL) {
		mwWrite("relink: both head and tail pointers damaged, aborting program\n");
		mwFlushW(1);
		FLUSH();
		abort();
	}
	if (mwHead == NULL) {
		mwHead = mw2;
		mwWrite("relink: heap truncated, MW-%p designated as new mwHead\n", mw2);
		mw2->prev = NULL;
		mw1 = mw2 = NULL;
	}
	if (mwTail == NULL) {
		mwTail = mw1;
		mwWrite("relink: heap truncated, MW-%p designated as new mwTail\n", mw1);
		mw1->next = NULL;
		mw1 = mw2 = NULL;
	}
	if (mw1 == NULL && mw2 == NULL &&
	    mwHead->prev == NULL && mwTail->next == NULL) {
		mwWrite("relink: verifying heap integrity...\n");
		FLUSH();
		goto verifyok;
	}
	if (mw1 && mw2 && mw1 != mw2) {
		mw1->next = mw2;
		mw2->prev = mw1;
		mwWrite("relink: emergency repairs successful, assessing damage...\n");
		FLUSH();
	} else {
		mwWrite("relink: heap totally destroyed, aborting program\n");
		mwFlushW(1);
		FLUSH();
		abort();
	}

	/* Verify by checking that the number of active allocations */
	/* match the number of entries in the chain */
      verifyok:
	if (!mwIsHeapOK(NULL)) {
		mwWrite("relink: heap verification FAILS - aborting program\n");
		mwFlushW(1);
		FLUSH();
		abort();
	}
	for (size = count = 0, mw1 = mwHead; mw1; mw1 = mw1->next) {
		count++;
		size += (long) mw1->size;
	}
	if (count == mwNumCurAlloc) {
		mwWrite("relink: successful, ");
		if (size == mwStatCurAlloc) {
			mwWrite("no allocations lost\n");
		} else {
			if (mw != NULL) {
				mwWrite("size information lost for MW-%p\n", mw);
				mw->size = 0;
			}
		}
	} else {
		mwWrite("relink: partial, %ld MW-blocks of %ld bytes lost\n",
			mwNmlNumAlloc + mwNumCurAlloc - count, mwNmlCurAlloc + mwStatCurAlloc - size);
		return 0;
	}

	return 1;
}

/*
   **  If mwData* is NULL:
   **      Returns 0 if heap chain is broken.
   **      Returns 1 if heap chain is intact.
   **  If mwData* is not NULL:
   **      Returns 0 if mwData* is missing or if chain is broken.
   **      Returns 1 if chain is intact and mwData* is found.
 */
static int mwIsHeapOK(mwData * includes_mw)
{
	int found = 0;
	mwData *mw;

	for (mw = mwHead; mw; mw = mw->next) {
		if (includes_mw == mw)
			found++;
		if (!mwIsSafeAddr(mw, sizeof(mwData)))
			return 0;
		if (mw->prev) {
			if (!mwIsSafeAddr(mw->prev, sizeof(mwData)))
				return 0;
			if (mw == mwHead || mw->prev->next != mw)
				return 0;
		}
		if (mw->next) {
			if (!mwIsSafeAddr(mw->next, sizeof(mwData)))
				return 0;
			if (mw == mwTail || mw->next->prev != mw)
				return 0;
		} else if (mw != mwTail)
			return 0;
	}

	if (includes_mw != NULL && !found)
		return 0;

	return 1;
}

static int mwIsOwned(mwData * mw, const char *file, int line)
{
	int retv;
	mwStat *ms;

	/* see if the address is legal according to OS */
	if (!mwIsSafeAddr(mw, sizeof(mwData)))
		return 0;

	/* make sure we have _anything_ allocated */
	if (mwHead == NULL && mwTail == NULL && mwStatCurAlloc == 0)
		return 0;

	/* calculate checksum */
	if (mw->check != CHKVAL(mw)) {
		/* may be damaged checksum, see if block is in heap */
		if (mwIsHeapOK(mw)) {
			/* damaged checksum, repair it */
			mwWrite("internal: <%ld> %s(%d), checksum for MW-%p is incorrect\n",
				mwCounter, file, line, mw);
			mwIncErr();
			if (mwIsReadAddr(mw->file, 1)) {
				ms = mwStatGet(mw->file, -1, 0);
				if (ms == NULL)
					mw->file = "<relinked>";
			} else
				mw->file = "<unknown>";
			mw->check = CHKVAL(mw);
			return 1;
		}
		/* no, it's just some garbage data */
		return 0;
	}
	/* check that the non-NULL pointers are safe */
	if (mw->prev && !mwIsSafeAddr(mw->prev, sizeof(mwData)))
		mwRelink(mw, file, line);
	if (mw->next && !mwIsSafeAddr(mw->next, sizeof(mwData)))
		mwRelink(mw, file, line);

	/* safe address, checksum OK, proceed with heap checks */

	/* see if the block is in the heap */
	retv = 0;
	if (mw->prev) {
		if (mw->prev->next == mw)
			retv++;
	} else {
		if (mwHead == mw)
			retv++;
	}
	if (mw->next) {
		if (mw->next->prev == mw)
			retv++;
	} else {
		if (mwTail == mw)
			retv++;
	}
	if (mw->check == CHKVAL(mw))
		retv++;
	if (retv > 2)
		return 1;

	/* block not in heap, check heap for corruption */

	if (!mwIsHeapOK(mw)) {
		if (mwRelink(mw, file, line))
			return 1;
	}
	/* unable to repair */
	mwWrite("internal: <%ld> %s(%d), mwIsOwned fails for MW-%p\n",
		mwCounter, file, line, mw);
	mwIncErr();

	return 0;
}

/*
   ** mwTestBuf:
   **  Checks a buffers links and pre/postfixes.
   **  Writes errors found to the log.
   **  Returns zero if no errors found.
 */
static int mwTestBuf(mwData * mw, const char *file, int line)
{
	int retv = 0;
	char *p;
	long chk;

	if (file == NULL)
		file = "unknown";

	if (!mwIsSafeAddr(mw, sizeof(mwData))) {
		mwWrite("internal: <%ld> %s(%d): pointer MW-%p is invalid\n",
			mwCounter, file, line, mw);
		mwIncErr();
		return 2;
	}
	if (mw->check != CHKVAL(mw)) {
		mwWrite("internal: <%ld> %s(%d), info trashed; relinking\n",
			mwCounter, file, line);
		mwIncErr();
		if (!mwRelink(mw, file, line))
			return 2;
	}
	if (mw->prev && mw->prev->next != mw) {
		mwWrite("internal: <%ld> %s(%d), buffer <%ld> %s(%d) link1 broken\n",
			mwCounter, file, line, (long) mw->size, mw->count, mw->file, mw->line);
		mwIncErr();
		if (!mwRelink(mw, file, line))
			retv = 2;
	}
	if (mw->next && mw->next->prev != mw) {
		mwWrite("internal: <%ld> %s(%d), buffer <%ld> %s(%d) link2 broken\n",
			mwCounter, file, line, (long) mw->size, mw->count, mw->file, mw->line);
		mwIncErr();
		if (!mwRelink(mw, file, line))
			retv = 2;
	}
	p = (char *) (mw + 1);
	GETDWORD(chk, p);
	if (chk != PRECHK) {
		mwWrite("underflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n",
			mwCounter, file, line, (long) mw->size, mw->count, mw->file, mw->line);
		mwIncErr();
		retv = 1;
	}
	p += mw->size + sizeof(long);
	GETDWORD(chk, p);
	if (chk != POSTCHK) {
		mwWrite("overflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n",
			mwCounter, file, line, (long) mw->size, mw->count, mw->file, mw->line);
		mwIncErr();
		retv = 1;
	}
	return retv;
}

static void mwDefaultOutFunc(int c)
{
	if (mwLogR())
		fputc(c, mwLogR());
}

static void mwWrite(const char *format,...)
{
	int tot, oflow = 0;
	va_list mark;
	mwAutoInit();
	if (mwOutFunction == NULL)
		mwOutFunction = mwDefaultOutFunc;
	va_start(mark, format);
	tot = vsprintf(mwPrintBuf, format, mark);
	va_end(mark);
	if (tot >= MW_TRACE_BUFFER) {
		mwPrintBuf[MW_TRACE_BUFFER] = 0;
		oflow = 1;
	}
	for (tot = 0; mwPrintBuf[tot]; tot++)
		(*mwOutFunction) (mwPrintBuf[tot]);
	if (oflow) {
		mwWrite("\ninternal: mwWrite(): WARNING! OUTPUT EXCEEDED %u CHARS: SYSTEM UNSTABLE\n", MW_TRACE_BUFFER - 1);
		FLUSH();
	}
	return;
}

static void mwLogFile(const char *name)
{
	time_t tid;
	(void) time(&tid);
	if (mwLogR() != NULL) {
		fclose(mwLogR());
		mwLogW(NULL);
	}
	if (name == NULL)
		return;
	mwLogW(fopen(name, "a" COMMIT));
	if (mwLogR() == NULL)
		mwWrite("logfile: failed to open/create file '%s'\n", name);
}

/*
   ** Try to free NML memory until a contiguous allocation of
   ** 'needed' bytes can be satisfied. If this is not enough
   ** and the 'urgent' parameter is nonzero, grabbed memory is
   ** also freed.
 */
static size_t mwFreeUp(size_t needed, int urgent)
{
	void *p;
	mwData *mw, *mw2;
	char *data;

	/* free grabbed NML memory */
	for (;;) {
		if (mwDrop_(1, MW_VAL_NML, 1) == 0)
			break;
		p = malloc(needed);
		if (p == NULL)
			continue;
		free(p);
		return needed;
	}

	/* free normal NML memory */
	mw = mwHead;
	while (mw != NULL) {
		if (!(mw->flag & MW_NML))
			mw = mw->next;
		else {
			data = ((char *) (mw + 1)) + sizeof(long);
			if (mwTestMem(data, mw->size, MW_VAL_NML)) {
				mwIncErr();
				mwWrite("wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
					mw->count, data + sizeof(long), mw->file, mw->line);
			}
			mw2 = mw->next;
			mwUnlink(mw, "mwFreeUp", 0);
			free(mw);
			mw = mw2;
			p = malloc(needed);
			if (p == NULL)
				continue;
			free(p);
			return needed;
		}
	}

	/* if not urgent (for internal purposes), fail */
	if (!urgent)
		return 0;

	/* free grabbed memory */
	for (;;) {
		if (mwDrop_(1, MW_VAL_GRB, 1) == 0)
			break;
		p = malloc(needed);
		if (p == NULL)
			continue;
		free(p);
		return needed;
	}

	return 0;
}

static const void *mwTestMem(const void *p, unsigned len, int c)
{
	const unsigned char *ptr;
	ptr = (const unsigned char *) p;
	while (len--) {
		if (*ptr != (unsigned char) c)
			return (const void *) ptr;
		ptr++;
	}
	return NULL;
}

static int mwStrCmpI(const char *s1, const char *s2)
{
	if (s1 == NULL || s2 == NULL)
		return 0;
	while (*s1) {
		if (toupper(*s2) == toupper(*s1)) {
			s1++;
			s2++;
			continue;
		}
		return 1;
	}
	return 0;
}

static int mwTestNow(const char *file, int line, int always_invoked)
{
	int retv = 0;
	mwData *mw;
	char *data;

	if (file && !always_invoked)
		mwWrite("check: <%ld> %s(%d), checking %s%s%s\n",
			mwCounter, file, line,
			(mwTestFlags & MW_TEST_CHAIN) ? "chain " : "",
			(mwTestFlags & MW_TEST_ALLOC) ? "alloc " : "",
			(mwTestFlags & MW_TEST_NML) ? "nomansland " : ""
		    );

	if (mwTestFlags & MW_TEST_CHAIN) {
		for (mw = mwHead; mw; mw = mw->next) {
			if (!mwIsSafeAddr(mw, sizeof(mwData))) {
				mwWrite("check: heap corruption detected\n");
				mwIncErr();
				return retv + 1;
			}
			if (mw->prev) {
				if (!mwIsSafeAddr(mw->prev, sizeof(mwData))) {
					mwWrite("check: heap corruption detected\n");
					mwIncErr();
					return retv + 1;
				}
				if (mw == mwHead || mw->prev->next != mw) {
					mwWrite("check: heap chain broken, prev link incorrect\n");
					mwIncErr();
					retv++;
				}
			}
			if (mw->next) {
				if (!mwIsSafeAddr(mw->next, sizeof(mwData))) {
					mwWrite("check: heap corruption detected\n");
					mwIncErr();
					return retv + 1;
				}
				if (mw == mwTail || mw->next->prev != mw) {
					mwWrite("check: heap chain broken, next link incorrect\n");
					mwIncErr();
					retv++;
				}
			} else if (mw != mwTail) {
				mwWrite("check: heap chain broken, tail incorrect\n");
				mwIncErr();
				retv++;
			}
		}
	}
	if (mwTestFlags & MW_TEST_ALLOC) {
		for (mw = mwHead; mw; mw = mw->next) {
			if (mwTestBuf(mw, file, line))
				retv++;
		}
	}
	if (mwTestFlags & MW_TEST_NML) {
		for (mw = mwHead; mw; mw = mw->next) {
			if ((mw->flag & MW_NML)) {
				data = ((char *) (mw + 1)) + sizeof(long);
				if (mwTestMem(data, mw->size, MW_VAL_NML)) {
					mwIncErr();
					mwWrite("wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
						mw->count, data + sizeof(long), mw->file, mw->line);
				}
			}
		}
	}
	if (file && !always_invoked && !retv)
		mwWrite("check: <%ld> %s(%d), complete; no errors\n",
			mwCounter, file, line);
	return retv;
}

/**********************************************************************
** Statistics
**********************************************************************/

static void mwStatReport()
{
	mwStat *ms, *ms2;
	/* global statistics report */
	mwWrite("\nMemory usage statistics (global):\n");
	mwWrite(" N)umber of allocations made: %ld\n", mwStatNumAlloc);
	mwWrite(" L)argest memory usage      : %ld\n", mwStatMaxAlloc);
	mwWrite(" T)otal of all alloc() calls: %ld\n", mwStatTotAlloc);
	mwWrite(" U)nfreed bytes totals      : %ld\n", mwStatCurAlloc);

	if (mwStatLevel < 1)
		return;

	/* on a per-module basis */
	mwWrite("\nMemory usage statistics (detailed):\n");
	for (ms = mwStatList; ms; ms = ms->next) {
		if (ms->line == -1) {
			mwWrite(" module '%s' \t(N=%ld \tL=%ld \tT=%ld \tU=%ld)\n",
				ms->file ? ms->file : "<unknown>", ms->num, ms->max, ms->total, ms->curr);
			if (ms->file && mwStatLevel > 1) {
				for (ms2 = mwStatList; ms2; ms2 = ms2->next) {
					if (ms2->line != -1 && ms2->file != NULL &&
					!mwStrCmpI(ms2->file, ms->file)) {
						mwWrite("   line %d \t \t \t(N=%ld \tL=%ld \tT=%ld \tU=%ld)\n",
							ms2->line, ms2->num, ms2->max, ms2->total, ms2->curr);
					}
				}
			}
		}
	}
}

static mwStat *mwStatGet(const char *file, int line, int makenew)
{
	mwStat *ms;

	if (mwStatLevel < 2)
		line = -1;

	for (ms = mwStatList; ms != NULL; ms = ms->next) {
		if (line != ms->line)
			continue;
		if (file == NULL) {
			if (ms->file == NULL)
				break;
			continue;
		}
		if (ms->file == NULL)
			continue;
		if (!strcmp(ms->file, file))
			break;
	}

	if (ms != NULL)
		return ms;

	if (!makenew)
		return NULL;

	ms = (mwStat *) malloc(sizeof(mwStat));
	if (ms == NULL) {
		if (mwFreeUp(sizeof(mwStat), 0) < sizeof(mwStat) ||
		    (ms = (mwStat *) malloc(sizeof(mwStat))) == NULL) {
			mwWrite("internal: memory low, statistics incomplete for '%s'\n", file);
			return NULL;
		}
	}
	ms->file = file;
	ms->line = line;
	ms->total = 0L;
	ms->max = 0L;
	ms->num = 0L;
	ms->curr = 0L;
	ms->next = mwStatList;
	mwStatList = ms;
	return ms;
}

static void mwStatAlloc(size_t size, const char *file, int line)
{
	mwStat *ms;

	/* update the module statistics */
	ms = mwStatGet(file, -1, 1);
	if (ms != NULL) {
		ms->total += (long) size;
		ms->curr += (long) size;
		ms->num++;
		if (ms->curr > ms->max)
			ms->max = ms->curr;
	}
	/* update the line statistics */
	if (mwStatLevel > 1 && line != -1 && file) {
		ms = mwStatGet(file, line, 1);
		if (ms != NULL) {
			ms->total += (long) size;
			ms->curr += (long) size;
			ms->num++;
			if (ms->curr > ms->max)
				ms->max = ms->curr;
		}
	}
}

static void mwStatFree(size_t size, const char *file, int line)
{
	mwStat *ms;

	/* update the module statistics */
	ms = mwStatGet(file, -1, 1);
	if (ms != NULL)
		ms->curr -= (long) size;

	/* update the line statistics */
	if (mwStatLevel > 1 && line != -1 && file) {
		ms = mwStatGet(file, line, 1);
		if (ms != NULL)
			ms->curr -= (long) size;
	}
}

#if 0				/* 980317: disabled C++ */

/**********************************************************************
** C++ new & delete
**********************************************************************/

#ifdef __cplusplus
#ifndef MEMWATCH_NOCPP

int mwNCur = 0;
const char *mwNFile = NULL;
int mwNLine = 0;

class MemWatch {
	public:
	MemWatch();
	~MemWatch();
};

MemWatch: :MemWatch()
{
	if (mwInited)
		return;
	mwUseAtexit = 0;
	mwInit();
}

MemWatch: :~MemWatch()
{
	if (mwUseAtexit)
		return;
	mwTerm();
}

/*
   ** This global new will catch all 'new' calls where MEMWATCH is
   ** not active.
 */
void *operator new(unsigned size)
{
	mwNCur = 0;
	return mwMalloc(size, "<unknown>", 0);
}

/*
   ** This is the new operator that's called when a module uses mwNew.
 */
void *operator new(unsigned size, const char *file, int line)
{
	mwNCur = 0;
	return mwMalloc(size, file, line);
}

/*
   ** Since this delete operator will recieve ALL delete's
   ** even those from within libraries, we must accept
   ** delete's before we've been initialized. Nor can we
   ** reliably check for wild free's if the mwNCur variable
   ** is not set.
 */
void operator delete(void *p)
{
	if (p == NULL)
		return;
	if (!mwInited) {
		free(p);
		return;
	}
	if (mwNCur) {
		mwFree(p, mwNFile, mwNLine);
		mwNCur = 0;
		return;
	}
	mwFree_(p);
}

#endif				/* MEMWATCH_NOCPP */
#endif				/* __cplusplus */

#endif				/* 980317: disabled C++ */

/* MEMWATCH.C */
