// GSDebug.cpp
//
// Copyright 1999 Diehl Graphsoft, Inc.
// All Rights Reserved.
//
// BRIEF_DESCRIPTION_OF_WHAT_IS_IN_THIS_FILE.
//

#if defined(GS_MAC) || defined(DEV_BLD) // Added to create bogus release version of GSDebug lib. [VML 01/09/04]
#if _MINICAD_
	#include "SharedDefines.h"	// DO NOT MOVE THIS LINE FROM BEING THE FIRST NON-SYSTEM INCLUDE FILE.  [DMB, 4/10/96]
#else
	#include "CoreDefines.h"	// DO NOT MOVE THIS LINE FROM BEING THE FIRST NON-SYSTEM INCLUDE FILE.  [DMB, 4/10/96]
#endif

#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#if _WINDOWS
	#include <Windows.h>
	#include <Float.h>
#endif

#if _WINDOWS
	#include <QTML.h>
	#if !_GSDEBUG_DLL_
//		#include <UndoName.h>
	#endif
#endif

#include "GSDebug.h"

#include "MiniCadHookIntf.h"

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////


#if TEST
	#if !_MINICAD_EXTERNAL_ && !_GSESDK_ && !_GSWINSDK_
		#if _WINDOWS
			#if BUG && !_GSDEBUG_DLL_
				#define	_CRTDBG_MAP_ALLOC 1
				#include <crtdbg.h>
			#endif
		#else
			#define DEBUG_NEW	DEBUG_NEW_OFF
			#include <DebugNew.h>
			// undefine the DEBUG_NEW flag (needed in DebugNew.h) so we can redefine it as a macro
			#undef DEBUG_NEW
		#endif
	#endif

	#if _MINICAD_EXTERNAL_ || _GSESDK_
		#include "MiniCadCallbacks.h"
	#endif
#endif // TEST

#include "../Include/Interfaces/VectorWorks/Debug/IDebugSupport.h"

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

#if TEST

#ifndef WIN_ONLY
	#if _WINDOWS
		#define WIN_ONLY(x)  x
	#else
		#define WIN_ONLY(x)
	#endif
#endif

const size_t kBuf_size = 1024;

char gDebugAppName[32] = "";

#if BUG
	// Set by GS_DebugInit( ).
	static Uint32 gDebugFlags = GSDebug::kNoFlags;
#endif


/////////////////////////////////////////////////////////////////////////////////////////

#if _WINDOWS

// Instance handle of the application using GSDebug. Set by GS_DebugSetExeHandle().
extern "C" HINSTANCE gExeInstance = NULL;

void GDB_DebugSetExeHandle(HINSTANCE hInst)
{
	// Set the global handle to the calling application.
	gExeInstance = hInst;
}

#endif _WINDOWS

/////////////////////////////////////////////////////////////////////////////////////////

#if _WINDOWS

// The window to use as the parent of the next debug dialog.
static HWND g_hParentWnd = NULL;

void GDB_SetDebugMessageParentWindow(HWND hParentWnd)
{
	g_hParentWnd = hParentWnd;
}

#endif

/////////////////////////////////////////////////////////////////////////////////////////

static bool GSDebugRunningMacOSX(void)
{
#if GS_MAC
	return true;
#else
	return false;
#endif
}
/////////////////////////////////////////////////////////////////////////////////////////

DECLAREWHO(gProgrammerFilter, "Default Filter");


TProgrammer TProgrammer::operator &(const TProgrammer& a) const
{
	TProgrammer dest(*this);

	// Combine ID's
	dest.fID |= a.fID;


	// we don't want to combine names because that steps on the assert categories
	// passed to the debug server. We may find a better way to do this later, but
	// for now, the first programmer on the list is the primary programmer


	// the alternative here is to filter the special modifier bits in both the constructor
	// and in this code, but I'll leave that for later - PCP

	// Combine names.
	//lstrcat(lstrcat(dest.fName, " & "), a.fName);

	// Combine sending.
	if (a.fSendOnOwn) dest.fSendOnOwn = true;

	return dest;
}

bool TProgrammer::LogOnOwn() const
{
	return (_IsUser(*this) ? fSendOnOwn : true);
}

bool TProgrammer::ContainsCurrentUser() const
{
#if _MINICAD_EXTERNAL_ || _GSESDK_
	return true;
#else
	if (IsEveryone())
		return true;	// Save some time.  This will be a common occurance.

	for (int i = 0; i < fMaxProgrammers; ++i) {
		if (fRegistered[i].fID == 0)
			break;
		if (_IsUser(fRegistered[i].fName)) {
			if (fRegistered[i].fID & GetID(true))
				return true;
			break;
		}
	}
	return false;
#endif
}

const int TProgrammer::fMaxProgrammers = 56;
TProgrammer::TRegistered TProgrammer::fRegistered[fMaxProgrammers];

void TProgrammer::Register(const char *name, Uint64 id)
{
	id &= kEveryoneConst;
	if (id != kEveryoneConst) {
		// Find available slot.
		int i;
		for (i=0; i<fMaxProgrammers; ++i) {
			if (fRegistered[i].fID & id) return; // reject multiples
			if (fRegistered[i].fID == 0) break;
		}
		// Fill it in.
		if (i<fMaxProgrammers) {
			fRegistered[i].fID = id;
			lstrcpy(fRegistered[i].fName, name);
		}
	}
}

bool TProgrammer::GetProgrammerName(Uint64 id, char *name)
{
	for (int i=0; i<fMaxProgrammers; ++i) {
		if (fRegistered[i].fID == id) {
			lstrcpy(name, fRegistered[i].fName);
			return true;
		}
	}
	return false;
}

bool TProgrammer::ExtractProgrammer(TProgrammer &who, TProgrammer &one)
{
	Uint64 id=1;
	for (Uint32 i=0; i<fMaxProgrammers; ++i, id*=2) {
		if (who & id) {
			char name[kDebugStringLength];
			GetProgrammerName(id, name);
			one.Set(name, id, who.fSendOnOwn);
			who.fID &= ~id;
			return true;
		}
	}
	return false;
}


/////////////////////////////////////////////////////////////////////////////////////////
// We use this to cause paint messages not to be dispatched by the MessageBox we create

#if _WINDOWS

static bool gl_InDebugMessageBox = false;

extern "C" Bool8 GSDebug_IsInDebugMessage()
// This function must return 'Bool8' becuase it is used by C modules which to not
// support the 'bool' datatype.
{
	return gl_InDebugMessageBox ? kTrue : kFalse;
}

#endif // _WINDOWS


//////////////////////////////////////////////////////////////////////////////

static Bool8 _WantToStop(Bool8 BUG_ONLY(bStop))
// [DMB, 5/1/95]
{
	#if BUG
		#if _MINICAD_EXTERNAL_ || _GSDEBUG_DLL_ || _GSESDK_
			bStop;
			return true;
		#else
			static long level = 1;
			if (bStop) ++level; else --level;
			const bool previous = GSDebug::WantToBreak(level > 0);
			if (previous !=  GSDebug::WantToBreak()) {
				DMSG((kEveryone, brief, "Breaks have been %sabled.\n",  GSDebug::WantToBreak() ? "en" : "dis"));
			}
			return previous;
		#endif
	#else
		return false;
	#endif
}


/////////////////////////////////////////////////////////////////////////////////////////
// DebugLevel( )

/*

// when do my DMSG statements print?
//   left column id the level of various DMSG calls
//   top row is the global debug level (can be set in the debug dialog (command-'))
//
// NOTE - when the global state is set to dlNever, you should get NO debug output
//        except for ASSERT and DSTOP

                 gDebugLevel     dlAlways   brief   verbose   mongo   dlNever
DMSG level

dlAlways                            Y         Y         Y       Y        N
brief                               N         Y         Y       Y        N
verbose                             N         N         Y       Y        N
mongo                               N         N         N       Y        N
dlNever                             N         N         N       N        N

*/

//---------------------------------------------------------------------------------------
static EDebugLevel gDebugLevel = brief;

//---------------------------------------------------------------------------------------
EDebugLevel GSDebug::DebugLevel()
// Returns the current debug level setting.
{
	return gDebugLevel;
}

//---------------------------------------------------------------------------------------
static bool AllowDebugPrinting(EDebugLevel messageLevel)
// Returns whether messages with messageLevel should be printed.
// not in the namespace because it should only be used in the implementation of debugging code
{
	// this has changed behavior slightly to allow messages to be
	// completely turned off. If gDebugLevel is dlAlways, brief, verbose,
	// or mongo, then we will print messages as their permissions allow.
	// If gDebugLevel is dlNever, that guarantees that no messages get printed.
	return (gDebugLevel >= messageLevel) && gDebugLevel != dlNever;
}

//---------------------------------------------------------------------------------------
EDebugLevel GSDebug::DebugLevel(EDebugLevel newDebuglevel)
// Change the current debug level setting. Returns the previous debug level.
{
	const EDebugLevel savedDebugLevel = gDebugLevel;
	gDebugLevel = newDebuglevel;
	return savedDebugLevel;
}


/////////////////////////////////////////////////////////////////////////////////////////
// WantToDebug( )

//---------------------------------------------------------------------------------------
static bool gWantToDebug = true;

//---------------------------------------------------------------------------------------
bool GSDebug::WantToDebug()
// Returns the current WantToDebug setting.
{
	return gWantToDebug;
}

//---------------------------------------------------------------------------------------
bool GSDebug::WantToDebug(bool bWantToDebug)
// Change the current WantToDebug setting. Returns the previous setting.
{
	const bool savedWantToDebug = gWantToDebug;
	gWantToDebug = bWantToDebug;
	return savedWantToDebug;
}


/////////////////////////////////////////////////////////////////////////////////////////
// UseSourceDebugger

//---------------------------------------------------------------------------------------
static bool gUseSourceDebugger = true;

//---------------------------------------------------------------------------------------
bool GSDebug::UseSourceDebugger()
// Returns the current UseSourceDebugger setting.
{
	return gUseSourceDebugger;
}

//---------------------------------------------------------------------------------------
bool GSDebug::UseSourceDebugger(bool bUseSourceDebugger)
// Change the current UseSourceDebugger setting. Returns the previous setting.
{
	const bool savedUseSourceDebugger = gUseSourceDebugger;
	gUseSourceDebugger = bUseSourceDebugger;
	return savedUseSourceDebugger;
}


/////////////////////////////////////////////////////////////////////////////////////////
// WantToBreak

//---------------------------------------------------------------------------------------
static bool gWantToBreak = true;

//---------------------------------------------------------------------------------------
bool GSDebug::WantToBreak()
// Returns the current WantToBreak setting.
{
	return gWantToBreak;
}

//---------------------------------------------------------------------------------------
bool GSDebug::WantToBreak(bool bWantToBreak)
// Change the current WantToBreak setting. Returns the previous setting.
{
	const bool savedWantToBreak = gWantToBreak;
	gWantToBreak = bWantToBreak;
	return savedWantToBreak;
}


/////////////////////////////////////////////////////////////////////////////////////////
// DrawToScreen

//---------------------------------------------------------------------------------------
static bool gDrawToScreen = false;

//---------------------------------------------------------------------------------------
bool GSDebug::DrawToScreen()
// Returns the current DrawToScreen setting.
{
	return gDrawToScreen;
}

//---------------------------------------------------------------------------------------
bool GSDebug::DrawToScreen(bool bDrawToScreen)
// Change the current DrawToScreen setting. Returns the previous setting.
{
	const bool savedDrawToScreen = gDrawToScreen;
	gDrawToScreen = bDrawToScreen;
	return savedDrawToScreen;
}

//---------------------------------------------------------------------------------------

Uint32 GSDebug::Flags()
{
	return gDebugFlags;
}

//---------------------------------------------------------------------------------------

Uint32 GSDebug::Flags(Uint32 inNewFlags)
{
	gDebugFlags = inNewFlags;
	return gDebugFlags;
}


/////////////////////////////////////////////////////////////////////////////////////////
// DSTOP variables

//---------------------------------------------------------------------------------------
static ConstGSCStrPtr gDStopFileName = NULL;
static int gDStopLineNumber = -1;

//---------------------------------------------------------------------------------------
void GSDEBUG_EXPORT GSDebug::SetDStopFileName(ConstGSCStrPtr szFileName)
{
	gDStopFileName = szFileName;
}

//---------------------------------------------------------------------------------------
void GSDEBUG_EXPORT GSDebug::SetDStopLineNumber(int nLineNumber)
{
	gDStopLineNumber = nLineNumber;
}

#if _WINDOWS
/////////////////////////////////////////////////////////////////////////////////////////
static bool IsWindowsNT()
{
	OSVERSIONINFO os;
	os.dwOSVersionInfoSize = sizeof(os);
	GetVersionEx(&os);
	return os.dwPlatformId == VER_PLATFORM_WIN32_NT;
}
#endif


/////////////////////////////////////////////////////////////////////////////////////////
#if BUG
static bool ShouldShowDebugWindow(const TProgrammer& toWhom)
// Determine if a debug message box should be displayed.
{
	// Default to displaying it.
	bool bDisplayDebugWindow = true;

	if (_IsUser(toWhom) && !toWhom.ShouldShowWindow()) {
		// Don't show it if it belongs to the currently logged on user and
		// they have the 'kNoDebugWindowFlag' bit set.
		bDisplayDebugWindow = false;
	}
	else if (gDebugFlags & GSDebug::kNeverUseDebugWindows) {
		// Don't show it if the global 'kNeverUseDebugWindows' flag is set.
		bDisplayDebugWindow = false;
	}

	return bDisplayDebugWindow;
}
#endif // BUG

/////////////////////////////////////////////////////////////////////////////////////////


class OutputMessage {

	public:
		OutputMessage(bool sendToFile)
		{
		#if Paul | Jeff | AutoBuild
			if (sendToFile) {
				#if GS_WIN

				// get the path of the application and append the name of the debug output file
				char szDebugPath[MAX_PATH];
				GetModuleFileName(NULL, szDebugPath, MAX_PATH);

				size_t length = strlen(szDebugPath);

				for (size_t i = length; i > 0; i--) {
					if (szDebugPath[i] == '\\') {
						szDebugPath[i+1] = 0;
						break;
					}
				}

				strcat(szDebugPath, "VW debug output.txt");

				theFile = fopen(szDebugPath, "a+");

				#else
					theFile = fopen("VW debug output.txt", "a+");
				#endif
			}
			else
				theFile = nil;
		#else
			sendToFile;
			theFile = nil;
		#endif
		}

		~OutputMessage()
		{
			if(theFile)
				fclose(theFile);
		}


		void SendString(const char* s)
		{
			// this first section sends the string to the debug window of the IDE
		#if GS_MAC && (_MINICAD_EXTERNAL_ || _GSESDK_)
			// output to the debugwindow on the Mac for plug-ins
			void __SendToDebugWindow ( const char *stringToSend );

			__SendToDebugWindow(s);
		#else
			OutputDebugString(s);
		#endif


			// next we send it to the debug output file
			if(theFile)
				fprintf(theFile, "%s", s);
		}

	private:
		FILE* theFile;
};

/////////////////////////////////////////////////////////////////////////////////////////

bool __ShowAlertDialog(const char* szMsgText, const char* szFile = NULL, int line = 0)
{
	using namespace VectorWorks::Debug;

	if ( szFile == NULL )	szFile	= gDStopFileName;
	if ( line == 0 )		line	= gDStopLineNumber;

	VCOMPtr<IDebugSupport> pDebugTools( IID_DebugSupport );

	bool	bDoBreak	= false;
	if ( pDebugTools == NULL ) {
		bDoBreak	= true;
	}
	else {
		pDebugTools->ShouldBreak( szFile, line, bDoBreak );

		if ( bDoBreak ) {
			static char	fileText[ kBuf_size ];
			size_t	len		= sprintf( fileText, "%s(%d)", szFile, line );
			DSTOPIF(len>kBuf_size, (kEveryone, "MAYDAY!!!  Your assertion message is too big. IT IS CORRUPTING THE DATA SEGMENT."));

			short dlgRes	= 1;
			pDebugTools->ShowAlertDialog(
				szMsgText,
				fileText,
				0,
				"Break",
				"Close",
				"Never Show This",
				"Options...",
				dlgRes );

			if ( dlgRes == 0 ) {
				// Close
				bDoBreak	= false;
			}
			else if ( dlgRes == 1 ) {
				// Break
				bDoBreak	= true;
			}
			else if ( dlgRes == 2 ) {
				// Abort or Never Show This
				bDoBreak	= false;
				pDebugTools->StopBreaks( szFile, line );
			}
			else if ( dlgRes == 3 ) {
				// Options
				bDoBreak	= false;
				pDebugTools->ShowOptionsDialog();

				bool bWantToBreak	= true;
				pDebugTools->GetStopAtBreaks( bWantToBreak );
				GSDebug::WantToBreak( bWantToBreak );
			}
		}
	}
	return bDoBreak;
}


/////////////////////////////////////////////////////////////////////////////////////////

static Bool8 DoDebugMessage(const char* szFuncName, const TProgrammer &who, EDebugLevel level, Bool8 bFile, Bool8 bToDebugger, va_list &ap, const char *szFmt)
{
	Bool8 bStopOnReturn = false;

	// Change bFile and bToDebugger if they are true (DSTOP) and the flag to NOT use debug windows is set.
	//	Avoids crash on quit.
	if (bFile && bToDebugger && (gDebugFlags & GSDebug::kNeverUseDebugWindows)) {
		bFile = false;
		bToDebugger = false;
	}

	if (!who.IsActLikeRelease()) {

		#if _MINICAD_EXTERNAL_ || _GSESDK_
			level;
			Bool8 doStop = true;
		#elif defined(BUG)
			Bool8 doStop = GSDebug::WantToBreak();
		#else
			Bool8 doStop = false;
		#endif


		#if BUG && !_MINICAD_EXTERNAL_ && !_GSESDK_
		if (AllowDebugPrinting(level)  /*&& who.LooseIsIn(gProgrammerFilter)*/)
		#endif
		{
			// Determine if the message should be displayed or not.
			bool bDoTheMessage = false;
			if (bToDebugger) {
				// It's something like a DSTOP.
				if (who.IsOnlyIfActiveUserBitSet())
					bDoTheMessage = _IsUser(who);
				else
					bDoTheMessage = true;
			}
			else {
				// It's something like a DMSG.
				bDoTheMessage = _IsUser(who);
			}

			if (bDoTheMessage) {

				char szMsg[kBuf_size];
				if ( szFmt ) {
					VERIFYN(kDave, vsprintf(szMsg, szFmt, ap) < kBuf_size);
				}
				else {
					szMsg[0]	= 0;	// no string
				}
				va_end(ap);

				#if NETBUG
				// Send message to server in network debugging.
					if (bFile) {						// Check if message is destined for the server.
					#if !_MINICAD_EXTERNAL_ && !_GSESDK_
						if (bToDebugger && gDStopLineNumber > 0) {
							char msg2[kBuf_size + 128];
							sprintf(msg2, "DSTOP: File %s, Line %d\n  %s", gDStopFileName, gDStopLineNumber, szMsg);
							RemoteDebug(who, msg2);
						}
						else
					#endif
							RemoteDebug(who, szMsg);
					}
				#endif
				#if _WINDOWS && !_GSDEBUG_DLL_ && !_GSESDK_
					if (!bFile && bToDebugger && doStop && IsWindowsNT()) {
						Beep(1500, 20);
					}
				#endif

				#ifdef BUG
				// Send message to user.


					OutputMessage out(true);

					if (bToDebugger) {
						// Add a new-line character if not supplied one.
						if (szMsg[lstrlen(szMsg)-1] != '\n')
							lstrcat(szMsg, "\n");

						out.SendString("DEBUGSTOP DSTOP");

						// Output who the message is from.
						if (!who.IsEveryone()) {
							out.SendString("(");
							out.SendString((char *)who.GetName());
							out.SendString(")");
						}
						out.SendString(": ");
					}

					// Output the message.
					out.SendString(szMsg);

					if (bToDebugger && doStop && who.ShouldBreak()) {
						static char szMessage1[] = "%s: %s: %s";
						static char szMessage2[] = "%s: %s";
						static char szMessage3[] = "%s";
						static char szMessage4[] = "Unknown STOP";

						static char szAlertText[kBuf_size];
						int			len;
						if ( who != NULL && (szFuncName && szFuncName[0]) && szMsg != NULL )
							len = sprintf(szAlertText, (const char*) szMessage1, (const char*) who, szFuncName, szMsg);
						else if ( who != NULL && szMsg != NULL )
							len = sprintf(szAlertText, (const char*) szMessage2, (const char*) who, szMsg );
						else if ( who != NULL && (szFuncName && szFuncName[0]) )
							len = sprintf(szAlertText, (const char*) szMessage2, (const char*) who, szFuncName );
						else if ( (szFuncName && szFuncName[0]), szMsg != NULL )
							len = sprintf(szAlertText, (const char*) szMessage2, szFuncName, szMsg );
						else if ( szMsg != NULL )
							len = sprintf(szAlertText, (const char*) szMessage3, szMsg );
						else
							len = sprintf(szAlertText, "%s", (const char*) szMessage4 );
						DSTOPIF(len>511, (who, "MAYDAY!!!  Your assertion message is too big. IT IS CORRUPTING THE DATA SEGMENT."));

						bStopOnReturn = __ShowAlertDialog( szAlertText, gDStopFileName, gDStopLineNumber );
					}
				#endif
			}
		}
	}

	return bStopOnReturn;
}

inline static Bool8 DoDebugMessage(const char *szFuncName, EDebugLevel level, Bool8 bFile, Bool8 bToDebugger, va_list &ap, const char *szFmt)
{ return DoDebugMessage(szFuncName, kEveryone, level, bFile, bToDebugger, ap, szFmt); }

inline static Bool8 DoDebugMessage(const char *szFuncName, const TProgrammer &who, Bool8 bFile, Bool8 bToDebugger, va_list &ap, const char *szFmt)
{ return DoDebugMessage(szFuncName, who, dlAlways, bFile, bToDebugger, ap, szFmt); }

inline static Bool8 DoDebugMessage(const char *szFuncName, Bool8 bFile, Bool8 bToDebugger, va_list &ap, const char *szFmt)
{ return DoDebugMessage(szFuncName, kEveryone, dlAlways, bFile, bToDebugger, ap, szFmt); }

static const char *const DebugMsgNames[] = {"DMSG", "DMSG w/IF", "DSTOP", "DSTOP w/IF"};

#if !_MAGICFOLDER_
// These functions do not require a TProgrammer parameter.

#if BUG && __EXTERNDEFINES__
void GSDEBUG_EXPORT __DebugMessage(EDebugLevel level, const char *szFmt, ...);
void GSDEBUG_EXPORT __DebugMessage(const char *szFmt, ...);
void GSDEBUG_EXPORT __DebugMessage(Bool8 exp, EDebugLevel level, const char *szFmt, ...);
void GSDEBUG_EXPORT __DebugMessage(Bool8 exp, const char *szFmt, ...);
#endif

void __DebugMessage(EDebugLevel level, const char *szFmt, ...)
{
	va_list ap;
	va_start(ap, szFmt);
	DoDebugMessage(DebugMsgNames[0], level, false, false, ap, szFmt);
}

void __DebugMessage(const char *szFmt, ...)
{
	va_list ap;
	va_start(ap, szFmt);
	DoDebugMessage(DebugMsgNames[0], false, false, ap, szFmt);
}

#if 0
void __DebugMessage(Bool8 exp, EDebugLevel level, const char *szFmt, ...)
{
	if (exp) {
		va_list ap;
		va_start(ap, szFmt);
		DoDebugMessage(DebugMsgNames[1], level, false, false, ap, szFmt);
	}
}

void __DebugMessage(Bool8 exp, const char *szFmt, ...)
{
	if (exp) {
		va_list ap;
		va_start(ap, szFmt);
		DoDebugMessage(DebugMsgNames[1], false, false, ap, szFmt);
	}
}
#endif

#endif // !_MAGICFOLDER_

void __DebugMessage(const TProgrammer &toWhom, EDebugLevel level, const char *szFmt, ...)
{
	va_list ap;
	va_start(ap, szFmt);
	DoDebugMessage(DebugMsgNames[0], toWhom, level, false, false, ap, szFmt);
}

void __DebugMessage(EDebugLevel level, const TProgrammer& toWhom, const char *szFmt, ...)
{
	va_list ap;
	va_start(ap, szFmt);
	DoDebugMessage(DebugMsgNames[0], toWhom, level, false, false, ap, szFmt);
}

void __DebugMessage(const TProgrammer &toWhom, const char *szFmt, ...)
{
	va_list ap;
	va_start(ap, szFmt);
	DoDebugMessage(DebugMsgNames[0], toWhom, false, false, ap, szFmt);
}

#if 0
void __DebugMessage(Bool8 exp, const TProgrammer& toWhom, EDebugLevel level, const char *szFmt, ...)
{
	if (exp) {
		va_list ap;
		va_start(ap, szFmt);
		DoDebugMessage(DebugMsgNames[1], toWhom, level, false, false, ap, szFmt);
	}
}

void __DebugMessage(Bool8 exp, EDebugLevel level, const TProgrammer& toWhom, const char *szFmt, ...)
{
	if (exp) {
		va_list ap;
		va_start(ap, szFmt);
		DoDebugMessage(DebugMsgNames[1], toWhom, level, false, false, ap, szFmt);
	}
}

void __DebugMessage(Bool8 exp, const TProgrammer& toWhom, const char *szFmt, ...)
{
	if (exp) {
		va_list ap;
		va_start(ap, szFmt);
		DoDebugMessage(DebugMsgNames[1], toWhom, false, false, ap, szFmt);
	}
}
#endif

Bool8 __DebugMessageStop(const TProgrammer& toWhom, EDebugLevel level, const char *szFmt, ...)
{
	va_list ap;
	va_start(ap, szFmt);
	return DoDebugMessage(DebugMsgNames[2], toWhom, level, true, true, ap, szFmt);
}

Bool8 __DebugMessageStop(EDebugLevel level, const TProgrammer& toWhom, const char *szFmt, ...)
{
	va_list ap;
	va_start(ap, szFmt);
	return DoDebugMessage(DebugMsgNames[2], toWhom, level, true, true, ap, szFmt);
}

Bool8 __DebugMessageStop(EDebugLevel level, const char *szFmt, ...)
{
	va_list ap;
	va_start(ap, szFmt);
	return DoDebugMessage(DebugMsgNames[2], level, true, true, ap, szFmt);
}

Bool8 __DebugMessageStop(const TProgrammer& toWhom, const char *szFmt, ...)
{
	va_list ap;
	va_start(ap, szFmt);
	return DoDebugMessage(DebugMsgNames[2], toWhom, true, true, ap, szFmt);
}

#if 0
Bool8 __DebugMessageStop(Bool8 exp, const TProgrammer& toWhom, EDebugLevel level, const char *szFmt, ...)
{
	if (!exp) return false;

	va_list ap;
	va_start(ap, szFmt);
	return DoDebugMessage(DebugMsgNames[3], toWhom, level, true, true, ap, szFmt);
}

Bool8 __DebugMessageStop(Bool8 exp, EDebugLevel level, const TProgrammer& toWhom, const char *szFmt, ...)
{
	if (!exp) return false;

	va_list ap;
	va_start(ap, szFmt);
	return DoDebugMessage(DebugMsgNames[3], toWhom, level, true, true, ap, szFmt);
}

Bool8 __DebugMessageStop(Bool8 exp, EDebugLevel level, const char *szFmt, ...)
{
	if (!exp) return false;

	va_list ap;
	va_start(ap, szFmt);
	return DoDebugMessage(DebugMsgNames[3], level, true, true, ap, szFmt);
}

Bool8 __DebugMessageStop(Bool8 exp, const TProgrammer& toWhom, const char *szFmt, ...)
{
	if (!exp) return false;

	va_list ap;
	va_start(ap, szFmt);
	return DoDebugMessage(DebugMsgNames[3], toWhom, true, true, ap, szFmt);
}

Bool8 __DebugMessageStop(Bool8 exp, const char *szFmt, ...)
{
	if (!exp) return false;

	va_list ap;
	va_start(ap, szFmt);
	return DoDebugMessage(DebugMsgNames[3], true, true, ap, szFmt);
}
#endif


//////////////////////////////////////////////////////////////////////////////
// _IsUser

Bool8 _IsUser(const TProgrammer& who)
{
	#if _MINICAD_EXTERNAL_ || _GSESDK_
		who;
		return true;
	#else
		return who.ContainsCurrentUser();
	#endif
}

Bool8 _IsUser(const char *szUser)
// [DMB, 5/1/95]
{
#if !_MINICAD_EXTERNAL_ && !_GSESDK_
	const int	nBufferSize = 24;
	static char	szCurrentUser[nBufferSize] = "";

	if (szCurrentUser[0] == 0) {
		#if _WINDOWS
			Uint32 size = nBufferSize;
			if (!GetUserName(szCurrentUser, &size)) {
				if (!GetEnvironmentVariable("USERNAME", szCurrentUser, nBufferSize))
					lstrcpy(szCurrentUser, "error");
			}
		#else

			CFStringRef cfstrUserName;
			cfstrUserName = CSCopyUserName(true);
			if (cfstrUserName) {
				CFStringGetCString(cfstrUserName, szCurrentUser, sizeof(szCurrentUser), kCFStringEncodingMacRoman);
				CFRelease(cfstrUserName);
			}
			else {
				strcpy(szCurrentUser, "error");
			}
		#endif
	}

	return _stricmp(szUser, szCurrentUser) == 0;
#else
	szUser;
	return true;
#endif
}

//////////////////////////////////////////////////////////////////////////////
#if BUG && !_MINICAD_EXTERNAL_ && !_GSESDK_

TDumpDevice::TDumpDevice()
{
	fIndent = 0;
	fWho = kEveryone;
}

int TDumpDevice::printf(const TProgrammer &toWhom, int indent, const char *fmt, ...) const
{
	va_list marker;
	va_start(marker, fmt);
	vprintf(toWhom, indent, fmt, marker);
	return 0;
}

int TDumpDevice::printf(const char *fmt, ...) const
{
	va_list marker;
	va_start(marker, fmt);
	vprintf(fWho, fIndent, fmt, marker);
	return 0;
}


TTextDumpDevice gl_stdDumpDevice;

int TTextDumpDevice::vprintf(const TProgrammer &who, int indent, const char *fmt, va_list &params) const
{
	// Indent the format string.
	const int kBufferSize = 256;
	const int extra = indent * 3;
	const int len = lstrlen(fmt) + extra + 1;
	if (len > kBufferSize)
		DSTOP((kDave & kNoBreak, "TTextDumpDevice::printf(): Someone exceeded buffer limit: %d > %d", len, kBufferSize));
	char indentedStr[kBufferSize];
	memset(indentedStr, ' ', extra);
	lstrcpyn(&indentedStr[extra], fmt, kBufferSize - extra);

	DoDebugMessage("TTextDumpDevice::printf", who, false, false, params, indentedStr);
	return 0;
}

#endif // BUG

//////////////////////////////////////////////////////////////////////////////

#if _WINDOWS && BUG

#if !_GSDEBUG_DLL_ && !_GSESDK_

int __cdecl RedirectCRTDebugOutput(int nReportType, char* szMsg, int* pnRet)
{
	int nRet = FALSE;

	static bool lastCRTMessageWasTerminated = true;
	static int lastCRTMessageType = _CRT_WARN;

	switch (nReportType)
	{
		case _CRT_ASSERT:
		{
			DSTOP((kPaul, "CRT ASSERT: %s", szMsg));
			break;
		}

		case _CRT_WARN:
		{
			if (lastCRTMessageWasTerminated || lastCRTMessageType != nReportType) {
				DMSG(("CRT WARN : "));
			}

			DMSG(("%s", szMsg));
			break;
		}

		case _CRT_ERROR:
		{
			if (lastCRTMessageWasTerminated || lastCRTMessageType != nReportType) {
				DMSG(("CRT ERROR : "));
			}

			DMSG(("%s", szMsg));
			break;
		}

		default:
		{
			DSTOP((kPaul, "Unexpected CRT report type: %d", nReportType));
			break;
		}
	}

	long msgSize = strlen(szMsg);
	lastCRTMessageWasTerminated = (szMsg[msgSize-1] == '\n');
	lastCRTMessageType = nReportType;

	if   (pnRet)
		*pnRet = 0;

	return   nRet;
}
#endif




void InitDebugCRT()
// This function sets C Run-Time library debug heap flags.
{
#if !_GSDEBUG_DLL_ && !_GSESDK_
	// Get the current state of the flag.
	int flags = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );

	//
	// Below are the various flags you can use.  The top choice is always the
	// most thorough.
	//

	#if 1
		// Store additional debug information with CRT heap allocations.
		flags |= _CRTDBG_ALLOC_MEM_DF;
	#else
		// Do NOT store additional debug information with CRT heap allocations.
		// This turns off all CRT heap checking.
		flags &= ~_CRTDBG_ALLOC_MEM_DF;
	#endif

	#if 0
		// Check the CRT heap after every allocation and deallocation request.
		flags |= _CRTDBG_CHECK_ALWAYS_DF;
	#else
		// Only check the heap only during ASSERVALIDHEAP().
		flags &= ~_CRTDBG_CHECK_ALWAYS_DF;
	#endif

	#if 0
		// Include CRT internal memory blocks in heap validation.
		flags |= _CRTDBG_CHECK_CRT_DF;
	#else
		// Exclude CRT internal memory blocks in heap validation.
		flags &= ~_CRTDBG_CHECK_CRT_DF;
	#endif

	#if 0
		// Fill freed blocks with 0xDD and keep in CRT heap list.
		flags |= _CRTDBG_DELAY_FREE_MEM_DF;
	#else
		// Do not keep freed blocks in CRT heap list.
		flags &= ~_CRTDBG_DELAY_FREE_MEM_DF;
	#endif

	// Do leak detection at program exit.
	flags |= _CRTDBG_LEAK_CHECK_DF;

	// Set the new state for the flag
	_CrtSetDbgFlag(flags);


	_CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, RedirectCRTDebugOutput);

	// since our hook function is now handling these, turn off the default handling so
	// we don't get two copies of each message
	_CrtSetReportMode( _CRT_ASSERT, 0 );
	_CrtSetReportMode( _CRT_WARN, 0 );
	_CrtSetReportMode( _CRT_ERROR, 0 );


#endif // _GSDEBUG_DLL_
}
#endif // _WINDOWS && BUG


#if !_MINICAD_EXTERNAL_ && !_GSESDK_

GSDEBUG_EXPORT void GDB_DebugInit (const char* appName, Uint32 flags)
{
	short	nameLen = sizeof(gDebugAppName);
	strncpy(gDebugAppName, appName, nameLen);
	gDebugAppName[nameLen-1] = '\0';  // For safety

	#ifdef BUG
		GSDebug::WantToDebug(false);
		GSDebug::DebugLevel(brief);
		GSDebug::DrawToScreen(false);

		gDebugFlags = flags;
	#endif

	#if _WINDOWS && BUG
		InitDebugCRT();
	#endif

	#if NETBUG
		LogVersionStamp();
	#endif

	#if !_GSDEBUG_DLL_
		#ifdef BUG
			InitBugMem();
		#endif

		#if TEST
			PerformLaunchAsserts();
		#endif
	#endif

	#if GS_MAC && BUG && !_MINICAD_EXTERNAL_ && GS_MAC_CFM
		// Ignore memory allocated before this point (presumably
		//  mostly in static constructors and MSL libraries)
		DebugNewForgetLeaks();
	#endif
} // GDB_DebugInit

void GSDEBUG_EXPORT GDB_DebugTerminate()
{
	#if NETBUG
		// this is really ugly & we'll have to streamline the API in the future, but it solves the
		// immediate problem in all cases. We need to de-allocate RemoteDebug memory here, before
		// we check for leaks, but it will be re-allocated if there are leaks reported by the DSTOP below
		KillRemoteDebug();
	#endif

	#if _WINDOWS && BUG
		// Check for memory leaks in the C run-time heap.
		//   RMEOVE this for a while.  There is a bug in the Microsoft
		//   implementation of typeid().name that leaves memory around
		//   (see Q140670 in KnowledgeBase).  [DMB 4/30/96]
		//DSTOP((_CrtDumpMemoryLeaks(), MAKEWHO("MemoryLeaks"), "C run-time heap memory leak(s) detected.  Check debug output window for more info."));
	#endif

	#if GS_MAC && BUG && !_MINICAD_EXTERNAL_ && GS_MAC_CFM
		DebugNewValidateAllBlocks();
		long numLeaks = DebugNewReportLeaks();

		#if TEST
		if (numLeaks > 0)
			DSTOP((MAKEWHO("MemoryLeaks"), "DebugNew reported %ld leaks. See 'Leaks.log' in your MiniCad directory for details.\n", numLeaks));
		#endif
	#endif
} // GDB_DebugTerminate

#endif


#if _MINICAD_EXTERNAL_ || _GSESDK_

/*

Here's how all this works:

The external includes GSDebug.h which defines debugging macros to call these bottlenecks for the
appropriate test versions. These functions set up parameter passing blocks and pass the request to
Kludge, which then calls the real versions inside MiniCad

The implementations of the functions which receive these calls are in UCallBack.cp
*/


// the external debugging capability depends on gCBP being defined and maintained as a global
// this should already be done at global setup time


void RemoteLog(Uint16 level, const char *description)
{
	if (gCBP) {
		GS_Kludge(gCBP, kKludgeSendLog, (char*)description, nil);
	}
}

void CheckHeap(const TProgrammer &toWhom)
{
	if (gCBP) {
		GS_Kludge(gCBP, kKludgeCheckHeap, (char *)toWhom.GetName(), nil);
	}
}

void RemoteDebug(const char *toWhom, const char *description, Bool8 /*UNUSED(doStackCrawl)*/)
{
	AssertParameters pb;

	pb.toWhom = (char *)toWhom;
	pb.description = (char *)description;

	if (gCBP) {
		GS_Kludge(gCBP, kKludgeSendAssert, &pb, nil);
	}
}

void RemoteDebug(const TProgrammer& toWhom, const char *description)
{
	RemoteDebug(toWhom.GetName(), description, toWhom.ShouldDoStackCrawl());
}


// these asserts and verifys call back to MiniCad to actually do the verifications
// they are only defined for _MINICAD_EXTERNAL_ builds
// internal defs are in Builtins.cp

int _AssertValidPointer(const TProgrammer& toWhom, const char* file, int line, const char* itemName, const void *ptr, long lCount, Bool8 /*bReadOnly*/)
{
	ValidateParameters vp;
	long result = 0L;

	vp.toWhom = (char *)toWhom.GetName();
	vp.file = (char*)file;
	vp.line = line;
	vp.itemName = (char*)itemName;
	vp.thePointerOrHandle = (void*)ptr;
	vp.expectedSize = lCount;

	if (gCBP) {
		GS_Kludge(gCBP, kKludgeValidatePointer, &vp, &result);
	}
	return (int)result;
}

Bool8 _VerifyValidPointer(const TProgrammer& toWhom, const char* file, int line, const char* itemName, const void *ptr, long lCount, Bool8 /*bReadOnly*/)
{
	if (_AssertValidPointer(toWhom, file, line, itemName, ptr, lCount))
		STOP();
	return (ptr != nil);
}

int _AssertValidHandle(const TProgrammer& toWhom, const char* file, int line, const char* itemName, Handle h, long lCount)
{
	ValidateParameters vp;
	long result = 0L;

	vp.toWhom = (char *)toWhom.GetName();
	vp.file = (char*)file;
	vp.line = line;
	vp.itemName = (char*)itemName;
	vp.thePointerOrHandle = (void*)h;
	vp.expectedSize = lCount;

	if (gCBP) {
		GS_Kludge(gCBP, kKludgeValidateHandle, &vp, &result);
	}

	return (int)result;
}

Bool8 _VerifyValidHandle(const TProgrammer& toWhom, const char* file, int line, const char* itemName, Handle h, long lCount)
{
	if(_AssertValidHandle(toWhom, file, line, itemName, h, lCount))
		STOP();
	return (h != nil);
}

int _AssertValidString(Bool8 isAssert, const TProgrammer& toWhom, const char* file, int line, const char* itemName, const char *psz, long cbMin, long cbMax)
{
	ValidateParameters vp;
	long result = 0L;

	vp.toWhom = (char *)toWhom.GetName();
	vp.file = (char*)file;
	vp.line = line;
	vp.itemName = (char*)itemName;
	vp.thePointerOrHandle = (void*)psz;
	vp.expectedSize = cbMin;
	vp.expectedSize2 = cbMax;
	vp.isAssert = isAssert;

	if (gCBP) {
		GS_Kludge(gCBP, kKludgeValidateString, &vp, &result);
	}

	if(isAssert) {
		return (int)result;
	}
	else {
		// #dwd 4/10/00
		//if (result)
		//	STOP();
		return (psz != nil);
	}
}

int _AssertValidPString(Bool8 isAssert, const TProgrammer& toWhom, const char* file, int line, const char* itemName, ConstGSStringPtr ps, int cbMin, int cbMax)
{
	ValidateParameters vp;
	long result = 0L;

	vp.toWhom = (char *)toWhom.GetName();
	vp.file = (char*)file;
	vp.line = line;
	vp.itemName = (char*)itemName;
	vp.thePointerOrHandle = (void*)ps;
	vp.expectedSize = cbMin;
	vp.expectedSize2 = cbMax;
	vp.isAssert = isAssert;

	if (gCBP) {
		GS_Kludge(gCBP, kKludgeValidatePString, &vp, &result);
	}

	if(isAssert) {
		return (int)result;
	}
	else {
		// #dwd 4/10/00
		//if (result)
		//	STOP();
		return (ps != nil);
	}
}



#if DO_PORT_CHECKING && !_WINDOWS

int _AssertLegalPort(char *message, const char* file, int line, Bool8 allowWMgrPort)
{
	long result = 0L;
	ValidatePortParameters vp;

	vp.message = (char*)message;
	vp.file = (char*)file;
	vp.line = line;
	vp.flags = 0L;

	if (allowWMgrPort)
		vp.flags |= kAllowWMgrPort;

	if (gCBP) {
		GS_Kludge(gCBP, kKludgeCheckPorts, &vp, &result);
	}

	return (int)result;
}
#endif


#endif // _MINICAD_EXTERNAL_




//////////////////////////////////////////////////////////////////////////////
// _Log

#if NETBUG

int _Log(Uint16 level, const char *format, ...)
{
	va_list		ap;
	va_start(ap, format);

	static int		r = 0;	// 'static' so that it's off the stack and won't be messed up by an overflow of "buf[]".

	char		buf[kBuf_size];

	r = vsprintf(buf, format, ap);
	if (r > kBuf_size - 1) {
		#ifdef _WINDOWS

		#else
		GSBreakStrToLowLevelDebugger_("\p### logprintf: vsprintf overflowed buffer!!!!");
		#endif
	}
	va_end(ap);

	RemoteLog(level, buf);
	return r;
}


static void RemoteAssert(const TProgrammer& toWhom, const char *file, int line, const char *message)
{
	static int len;
	char buf[kBuf_size];

	#ifdef _WINDOWS
		if (message)
			len = sprintf(buf, "%s\r  file %s && line %d\r", message, file, line);
		else
			len = sprintf(buf, "  file %s && line %d\r", file, line);
	#else
		if (message)
			len = sprintf(buf, "%s\n  file %s && line %d\n", message, file, line);
		else
			len = sprintf(buf, "  file %s && line %d\n", file, line);
	#endif
	#if TEST
	if (len > kBuf_size-1)
		DSTOP((toWhom, "MAYDAY!!!  Your assertion message is too big. IT IS CORRUPTING DATA !!!"));
	#endif
	RemoteDebug(toWhom, buf);
}

#endif // NETBUG


// Function called when an ASSERTN, or VERIFYN fails.
int _FileDebugMessage(const TProgrammer& toWhom, const char *file, int line, const char *message)
{
	int ret = 0;

	if (!toWhom.IsActLikeRelease()) {
#ifdef _WINDOWS
		#ifdef NETBUG
			RemoteAssert(toWhom, file, line, message);
		#endif
#else
		#if NETBUG
			RemoteAssert(toWhom, file, line, message);
		#endif
#endif

		// Create string to show to user.
		static char szTitle[] = "Assertion Failed!";
		//static char szMessage1[] = "%s: File %s, Line %d,\n  %s";
		static char szMessage1[] = "%s: %s";
		static char szMessage2[] = "%s";
		static char szMessage3[] = "%s";
		static char szMessage4[] = "ASSERTN";
		static char szMsg[kBuf_size];

		int len;
		if (toWhom != NULL && message != NULL)
			len = sprintf(szMsg, (const char*) szMessage1, (const char*) toWhom, (const char*) message);
		else if (message != NULL)
			len = sprintf(szMsg, (const char*) szMessage2, (const char*) message);
		else if (message == NULL && toWhom != NULL)
			len = sprintf(szMsg, (const char*) szMessage3, (const char*) toWhom);
		else
			len = sprintf(szMsg, "%s", (const char*) szMessage4 );
		DSTOPIF(len>511, (toWhom, "MAYDAY!!!  Your assertion message is too big. IT IS CORRUPTING THE DATA SEGMENT."));

		// Write to debug window or serial port.

		OutputMessage out(true);

		out.SendString(szMsg);
		out.SendString(", ");
		out.SendString(szTitle);
		out.SendString("\n");

		#ifdef BUG

		ret = __ShowAlertDialog( szMsg, file, line );

		#endif
	}

	return ret;
}

// I know we are double buffering these messages, but I don't have the time right now to fix it
// and I don't expect it to cause any problems - PCP

int _Assert(const TProgrammer& toWhom, const char *file, int line, const char *expr)
{
	static int len;
	char buf[kBuf_size];

	len = sprintf(buf, "DEBUGSTOP ASSERT(%s) failed!", expr);
	if (len > kBuf_size-1)
		DSTOP((toWhom, "MAYDAY!!!  Your assertion message is too big. IT IS CORRUPTING DATA !!!"));

	return _FileDebugMessage(toWhom, file, line, buf);
}



// this function is available in the application as well as plug-ins - PCP
int GSDEBUG_EXPORT _AssertValidMenuHandle(const TProgrammer& toWhom, const char* file, int line, const char* itemName, GSHandle h)
{
	if (GSDebugRunningMacOSX()) {
		// Under MacOS X, MenuHandles are not handles, so there isn't much we can say about them - PCP
		return 0;
	}
	else {
		return _AssertValidHandle(toWhom, file, line, itemName, h);
	}
}





#if !_MINICAD_EXTERNAL_ && !_GSESDK_


// XXX_JDW_ALTURA [2003-05-21] - bogusness

// Can't use 'DO_PORT_CHECKING' here.
#if 0 && TEST && !_GSDEBUG_DLL_
Bool8 IsValidMacPort(GrafPtr port)
{
	#if _WINDOWS && !_GSDEBUG_DLL_
		////////////////////////////////////////

		if (VERIFYPOINTER(kChao, port, sizeof(CGrafPort))) {
			CGrafPtr p = (CGrafPtr)port;

			//short                           device;
			//PixMapHandle                    portPixMap;                 /*port's pixel map*/
			if (p->portPixMap)
				ASSERTHANDLE(kChao, p->portPixMap, sizeof(PixMap));
			//short                           portVersion;                /*high 2 bits always set*/
			//Handle                          grafVars;                   /*Handle to more fields*/
			if (p->grafVars)
				ASSERTHANDLE(kChao, p->grafVars, sizeof(GrafVars));
			//short                           chExtra;                    /*character extra*/
			//short                           pnLocHFrac;                 /*pen fraction*/
			//Rect                            portRect;
			//RgnHandle                       visRgn;
			if (VERIFYN(kChao, p->visRgn))
				ASSERTHANDLE(kChao, p->visRgn, sizeof(MacRegion));
			//RgnHandle                       clipRgn;
			if (VERIFYN(kChao, p->clipRgn))
				ASSERTHANDLE(kChao, p->clipRgn, sizeof(MacRegion));
			//PixPatHandle                    bkPixPat;                   /*background pattern*/
			if (p->bkPixPat)
				ASSERTHANDLE(kChao, p->bkPixPat, sizeof(PixPat));
			//RGBColor                        rgbFgColor;                 /*RGB components of fg*/
			//RGBColor                        rgbBkColor;                 /*RGB components of bk*/
			//Point                           pnLoc;
			//Point                           pnSize;
			ASSERTN(kChao, p->pnSize.h == p->pnSize.v && p->pnSize.h >= 0);
			//short                           pnMode;
			//PixPatHandle                    pnPixPat;                   /*pen's pattern*/
			if (p->pnPixPat)
				ASSERTHANDLE(kChao, p->pnPixPat, sizeof(PixPat));
			//PixPatHandle                    fillPixPat;                 /*fill pattern*/
			if (p->fillPixPat)
				ASSERTHANDLE(kChao, p->fillPixPat, sizeof(PixPat));
			//short                           pnVis;
			//short                           txFont;
			//StyleField                      txFace;                     /*StyleField occupies 16-bits, but only first 8-bits are used*/
			//UInt8                           txFlags;                    /* QuickTime uses second 8 bits of StyleField for txFlags */
			//short                           txMode;
			ASSERTN(kChao, p->txMode == srcOr || p->txMode == srcXor || p->txMode == srcBic);
			//short                           txSize;
			//Fixed                           spExtra;
			//long                            fgColor;
			//long                            bkColor;
			//short                           colrBit;
			//short                           patStretch;
			//Handle                          picSave;
			//Handle                          rgnSave;
			//Handle                          polySave;
			//CQDProcsPtr                     grafProcs;
		}

		return ( port ? true : false );

		// Dec 19, 2001 AlturaRemoval, jg
		//		return ASI_IsPortValid(port);

		////////////////////////////////////////
	#else
		return ( port ? true : false );
	#endif
}
#endif



#if _WINDOWS
///////////////////////////////////////////////////////////////////////////////

BOOL IsValidDC(HDC hdc)
// Try to validate an HDC.
// [DMB 6/25/96]
{
	BOOL bValid = FALSE;

	if (hdc) {
		// Try to get the HWND associated with the HDC.
		HWND hwnd = WindowFromDC(hdc);
		if (hwnd) {
			// Window DC.  Validate by verifying that the associated HWND is valid.
			bValid = IsWindow(hwnd);
		}
		else {
			// Maybe memory DC or printer DC.  Validate by trying to get text
			// alignment setting.
			bValid = ( GetTextAlign(hdc) != GDI_ERROR );
		}
	}

	return bValid;
}

///////////////////////////////////////////////////////////////////////////////


bool GSDEBUG_EXPORT _ValidateHWND(const TProgrammer& toWhom, bool bAssert, const char* file, int line, const char* itemName, void *hWnd)
// This is the TEST version of the function to validate an HWND.  For the release version, see GSDebug,h,
{
	bool valid = IsWindow((HWND)hWnd);

	if (!valid) {
		char buf[kBuf_size];
		sprintf(buf, "%s(%s) failed!\n%s", bAssert ? "AssertHWND" : "VerifyHWND", itemName, "Invalid HWND");
		int ret = _FileDebugMessage(toWhom, file, line, buf);
		if (bAssert) {
			valid = (ret == 0);
		}
		else {
			if (ret) STOP();
			valid = (hWnd != NULL);
		}
	}
	return valid;
}


bool GSDEBUG_EXPORT _ValidateHDC(const TProgrammer& toWhom, bool bAssert, const char* file, int line, const char* itemName, void *hdc)
// This is the TEST version of the function to validate an HDC.  For the release version, see GSDebug,h,
{
	bool valid = IsValidDC((HDC)hdc);

	if (!valid) {
		char buf[kBuf_size];
		sprintf(buf, "%s(%s) failed!\n%s", bAssert ? "AssertHDC" : "VerifyHDC", itemName, "Invalid HDC");
		int ret = _FileDebugMessage(toWhom, file, line, buf);
		if (bAssert) {
			valid = (ret == 0);
		}
		else {
			if (ret) STOP();
			valid = (hdc != NULL);
		}
	}
	return valid;
}


#endif // _WINDOWS


enum  {
	kVPNoErr = 0,
	kVPNegativeCount,
	kVPPtrIsNil,
	kVPNotReadable,
	kVPNotWritable,
	kVPNotInAppHeap,
	kVPPStringTooLong,
	kVPStringMinSizeTooLong,
	kVPStringBadLength,
	kVPHandleIsPurgeable,
	kVPBadHWND,
	kVPBadHDC,
	kVPHandleIsNil = 100,
	kVPHandleIsMisaligned,
	kVPMasterPtrIsNil,
	kVPMasterPtrIsAllF,
	kVPMasterPtrIsMisaligned,
	kVPMemErrorReturnedError,
	kVPHandleBlockTooSmall,
	kVPHandleFailsOSTest,
	kVPHandlePointerBase = 200,
	kVPHandleNegativeCount,
	kVPHandlePtrIsNil,
	kVPHandleNotReadable,
	kVPHandleNotWritable,
	kVPHandleNotInAppHeap,
	kVPMasterPointerBase = 300,
	kVPMasterPointerNegativeCount,
	kVPMasterPointerPtrIsNil,
	kVPMasterPointerNotReadable,
	kVPMasterPointerNotWritable,
	kVPMasterPointerNotInAppHeap,
	kVPBadlyFormattedPath
};


class UserError {
	public:
		short myErr;
		#if _GSDEBUG_DLL_
			UserError()				{ myErr = kVPNoErr; }
		#else
			OSErr sysErr;
			UserError()				{ myErr = kVPNoErr; sysErr = noErr; }
		#endif

		Bool8 IsError(Bool8 bCheckPointer = false) const;
};

Bool8 UserError::IsError(Bool8 bCheckPointer) const
{
	if (bCheckPointer)
		return myErr == kVPPtrIsNil || myErr == kVPHandleIsNil;
	else
#if _GSDEBUG_DLL_
		return myErr != kVPNoErr;
#else
		return myErr != kVPNoErr || sysErr != noErr;
#endif
}


static UserError IsValidAddress(const void *ptr, long lCount, Bool8 WIN_ONLY(bReadOnly))
//
// ***  If you change any of the tests below (including the testing order),
//      please let David Bowman know so that he can update the documentation.
//
{
	const void *strippedptr = ptr;

	UserError result;

	if (lCount < 0) {
		DMSG((dlAlways, kEveryone, "IsValidAddress(): Bad size parameter. (lCount = %ld)\n", lCount));
		result.myErr = kVPNegativeCount;
		return result;
	}

	if (strippedptr == NULL) {
		DMSG((dlAlways, kEveryone, "IsValidAddress(): Pointer is nil. (ptr == nil)\n"));
		result.myErr = kVPPtrIsNil;
		return result;
	}

	#if _WINDOWS
		return result;
/* Note for now, this has been disabled, because some classes are not contiguous memory blocks as one might expect.
[TIM 8/21/07]
	// This is Windows code.  [DMB, 2/16/95]
		if (IsBadReadPtr(strippedptr, lCount)) {
			DMSG((dlAlways, kEveryone, "IsValidAddress(): Pointer is not readable.\n"));
			result.myErr = kVPNotReadable;
			return result;
		}
		// In our app, if we have read, we have write. [DMB 6/27/96]
		if (!bReadOnly && IsBadWritePtr((void*)strippedptr, (UINT)lCount)) {
			DMSG((dlAlways, kEveryone, "IsValidAddress(): Pointer is readable, but not writable.\n"));
			result.myErr = kVPNotWritable;
			return result;
		}
*/
//
// ***  If you change any of the tests above or below (including the testing order),
//      please let David Bowman know so that he can update the documentation.
//

	#else
		// GS_CARBON_REVISIT - Under MacOS X, we simply return that all addresses are good
		return result;
	#endif // _WINDOWS

	return result;
//
// ***  If you change any of the tests above (including the testing order),
//      please let David Bowman know so that he can update the documentation.
//
}

static Bool8 IsPurgeableHandle(GSHandle h)
{
	#if _GSDEBUG_DLL_
		return true;
	#else
		if (h) {
			char	hFlags = HGetState(h);
			return ((hFlags & 0x40) != 0);
		}

		return false;
	#endif
}

static UserError IsValidHandle(const GSHandle h, long lCount, Bool8 bReadOnly)
// If 'lCount' is zero or negative, handles that point to nil (*h==nil)
// will be considered valid.
//
// ***  If you change any of the tests below (including the testing order),
//      please let David Bowman know so that he can update the documentation.
//
{
	UserError result;
	UserError ptrResult;

	#if GS_WIN
	long alignment = 2;
	#else
	long alignment;
	// GS_CARBON_REVISIT - for some reason, this alignment check often fails under MacOS X
	//if (GSDebugRunningMacOSX())
	//	alignment = 1;
	//else
		alignment = 4;
	#endif

	if (h == NULL) {
		DMSG((kEveryone, dlAlways, "IsValidHandle(): Handle is nil. (h == nil)\n"));
		result.myErr = kVPHandleIsNil;
		return result;
	}

	if ((long)h % alignment) {
		DMSG((kEveryone, dlAlways, "IsValidHandle(): Handle val is not aligned to %d byte boundary.\n", alignment));
		result.myErr = kVPHandleIsMisaligned;
		return result;
	}
	ptrResult = IsValidAddress(h, sizeof(GSHandle), bReadOnly);

	if (ptrResult.myErr != kVPNoErr) {
		DMSG((kEveryone, dlAlways, "IsValidHandle(): Handle is invalid memory. (h == bad_mem)\n"));
		result.myErr = ptrResult.myErr + kVPHandlePointerBase;
		return result;
	}

	if (lCount > 0) {
		if (*h == NULL) {
			DMSG((kEveryone, dlAlways, "IsValidHandle(): Master Ptr is nil. (*h == nil)\n"));
			result.myErr = kVPMasterPtrIsNil;
			return result;
		}
	}

	else {		// lCount <= 0
		// Don't care if it points to nil.
		if (*h == NULL)
			return result;
		#if GS_WIN
		// under Altura, zero length blocks have a master pointer of 0x0000FFFF
		// i.e. - this is a handle where (h != nil && GetHandleSize(h) == 0)
		// on the mac, this is represented as 		h->masterPointer->[zero logical length memory block in the heap]
		// on Altura, it's 							h->0x0000ffff
		if (lCount == 0L && *h == (char*)0x0000FFFF)
			return result;
		#endif
		// It doesn't point to nil.  Change the byte count and continue.
		lCount = -lCount;
	}

	if (GSDebugRunningMacOSX()) {
		// GS_CARBON_REVISIT_PCP - this may be appropriate for other platforms

		if (*h == (char*)0xFFFFFFFF) {
			result.myErr = kVPMasterPtrIsAllF;
		}

		// GS_CARBON_REVISIT_PCP - this should probably also be called under CarbonLib
		//if (!::IsHandleValid(h)) {
		//	DMSG((kEveryone, dlAlways, "IsValidHandle(): MacOS call ::IsHandleValid failed on this handle.\n"));
		//	result.myErr = kVPHandleFailsOSTest;
		//	return result;
		//}
	}
	else {
		// GS_CARBON_REVISIT - these checks are too aggressive for MacOS X

		#if !_GSDEBUG_DLL_
		if ((unsigned long)*h % alignment) {

			#if _WINDOWS
			if (*h == (char*)0x0000FFFF && GetHandleSize(h) == 0L) {
				// under Altura, this is legal, so we will continue checking
			}
			else
			#endif
			{
				DMSG((dlAlways, "IsValidHandle(): Master Ptr val is not aligned to word boundary.\n"));
				result.myErr = kVPMasterPtrIsMisaligned;
				return result;
			}
		}

		long size = GetHandleSize(h);
		OSErr err = MemError();
		if (err != noErr) {
			DMSG((dlAlways, "IsValidHandle(): System does not know about the handle (err=%ld).\n", err));
			result.myErr = kVPMemErrorReturnedError;
			result.sysErr = err;
			return result;
		}

		if (size < lCount) {
			DMSG((dlAlways, "IsValidHandle(): Memory block is too small. (%ld != %ld)\n", lCount, size));
			result.myErr = kVPHandleBlockTooSmall;
			return result;
		}
		#endif

		if (IsPurgeableHandle(h)) {
			DMSG((kEveryone, dlAlways, "IsValidHandle(): Handle is purgeable\n"));
			result.myErr = kVPHandleIsPurgeable;
			return result;
		}

		ptrResult = IsValidAddress(*h, lCount, bReadOnly);
		if (ptrResult.myErr != kVPNoErr) {
			DMSG((kEveryone, dlAlways, "IsValidHandle(): Handle points to invalid memory. (*h == bad_mem)\n"));
			result.myErr = ptrResult.myErr + kVPMasterPointerBase;
			return result;
		}

	} // !GSDebugRunningMacOSX

//
// ***  If you change any of the tests above (including the testing order),
//      please let David Bowman know so that he can update the documentation.
//
	return result;
}

#if 0
Bool8 VerifyHandle(const void *ptr, long lCount)
{
	return ! IsValidHandle((GSHandle)ptr, lCount, false).IsError();
}
#endif

static UserError IsValidString(const char *psz, long cbMin, long cbMax)
{
	UserError	result;
	long		i;
#if !_WINDOWS
	long		minLen;
#endif

	// 20480 is just a really big number
	if (cbMax==0) cbMax = 20480;

	if (cbMin > cbMax) {
		result.myErr = kVPStringMinSizeTooLong;
		return result;
	}

	if (psz == NULL) {
		result.myErr = kVPPtrIsNil;
		return result;
	}

	#if _WINDOWS
		if (IsBadStringPtr(psz, cbMin==0 ? 1 : cbMin)) {
			result.myErr = kVPNotReadable;
			return result;
		}
	#else
		// This relies on the OS _not_ stopping us from reading beyond the
		// end of a block of memory.  The MacOS will let us do this.  This
		// makes the function easier to write and much faster to execute
		// since we don't have to check each character of the string to see
		// if it is in valid memory before we access it.
	#endif

	for (i=0; i<=cbMax && psz[i]; ++i) ;			// Find end of string.
	if (i<cbMin || i>cbMax) {
		result.myErr = kVPStringBadLength;
		return result;
	}

#if !_WINDOWS
	minLen = (cbMax < (i+1)) ? cbMax : (i+1);
	return IsValidAddress(psz, minLen, false);	// Check for read/write permission on memory.
#elif _MSC_VER < 1200
	return IsValidAddress(psz, min(cbMax, i+1), false);	// Check for read/write permission on memory.
#else
	return IsValidAddress(psz, min(cbMax, i+1), true);	// Check for read permission on memory.
#endif
}

static UserError IsValidPString(ConstGSStringPtr ps, int cbMin, int cbMax)
// [DMB, 3/9/95]
{
	UserError result;
	UserError ptrResult;

	if (cbMax == 0)
		cbMax = 255;						// Caller does not care about a maximum.
	else if (cbMax>255){
		result.myErr = kVPPStringTooLong;
		return result;
	}
	if (cbMin > cbMax){
		result.myErr = kVPStringMinSizeTooLong;
		return result;
	}
	if (ps == NULL) {
		result.myErr = kVPPtrIsNil;
		return result;
	}

#if _MSC_VER < 1200
	ptrResult = IsValidAddress(ps, 1, false);				// Test if length byte is in valid memory.
#else
	ptrResult = IsValidAddress(ps, 1, true);				// Test if length byte is in valid memory.
#endif
	if (ptrResult.myErr != kVPNoErr) {
		return ptrResult;
	}
#if _MSC_VER < 1200
	ptrResult = IsValidAddress(ps+1, ps[0], false);		// Test if string characters are in valid memory.
#else
	ptrResult = IsValidAddress(ps+1, ps[0], true);		// Test if string characters are in valid memory.
#endif
	if (ptrResult.myErr != kVPNoErr) {
		return ptrResult;
	}

	if (ps[0] < cbMin || ps[0] > cbMax) {			// Test if length falls within bounds.
		result.myErr = kVPStringBadLength;
		return result;
	}

	// Passed all of the tests.
	return result;
}


static int _SendErrorMessage(const TProgrammer& toWhom, const char* file, int line, const char* theAssert, const char* itemName, UserError theErr, const void* theData, long lCount)
{
	if (theErr.myErr == kVPNoErr)
		return 0;

	static int len;
	char buf[kBuf_size];
	char reason[512];
	const char* errString;

	Bool8 displayPtr = false;
	Bool8 displayMasterPtr = false;
	Bool8 specialError = false;

	switch (theErr.myErr) {
		case kVPNegativeCount:						specialError = true; break;
		case kVPPtrIsNil:							errString = "Pointer is NIL"; break;
		case kVPNotReadable:						errString = "Pointer is not readable"; displayPtr = true; break;
		case kVPNotWritable:						errString = "Pointer is not writable"; displayPtr = true; break;
		case kVPNotInAppHeap:						errString = "Pointer does not point into our partition"; displayPtr = true; break;

		case kVPPStringTooLong:						specialError = true; break;
		case kVPStringMinSizeTooLong:				specialError = true; break;
		case kVPStringBadLength:					specialError = true; break;

		case kVPHandleIsNil:						errString = "Handle is nil"; break;
		case kVPHandleIsMisaligned:					errString = "Handle is misaligned"; displayPtr = true; break;
		case kVPHandleFailsOSTest:					errString = "Handle fails operating system validity test"; displayPtr = true; break;
		case kVPMasterPtrIsNil:						errString = "Master Pointer is nil"; displayPtr = true; displayMasterPtr = true; break;
		case kVPMasterPtrIsAllF:					errString = "Master Pointer is 0xFFFFFFFF"; displayPtr = true; displayMasterPtr = true; break;
		case kVPMasterPtrIsMisaligned:				errString = "Master Pointer is misaligned"; displayPtr = true; displayMasterPtr = true; break;
		case kVPMemErrorReturnedError:				specialError = true; break;
		case kVPHandleBlockTooSmall:				specialError = true; break;

		case kVPHandleNegativeCount:				errString = "INTERNAL ERROR"; break;
		case kVPHandlePtrIsNil:						errString = "INTERNAL ERROR"; break;
		case kVPHandleNotReadable:					errString = "Handle is not readable"; displayPtr = true; break;
		case kVPHandleNotWritable:					errString = "Handle is not writable"; displayPtr = true; break;
		case kVPHandleNotInAppHeap:					errString = "Handle does not point into our partition"; displayPtr = true; break;

		case kVPHandleIsPurgeable:					errString = "Handle is purgeable"; displayPtr = true; displayMasterPtr = true; break;

		case kVPBadHWND:							errString = "Invalid HWND"; break;
		case kVPBadHDC:								errString = "Invalid HDC"; break;

		case kVPMasterPointerNegativeCount:			errString = "INTERNAL ERROR"; break;
		case kVPMasterPointerPtrIsNil:				errString = "INTERNAL ERROR"; break;
		case kVPMasterPointerNotReadable:			errString = "Master Pointer is not readable"; displayPtr = true; displayMasterPtr = true; break;
		case kVPMasterPointerNotWritable:			errString = "Master Pointer is not writable"; displayPtr = true; displayMasterPtr = true; break;
		case kVPMasterPointerNotInAppHeap:			errString = "Master Pointer does not point into our partition"; displayPtr = true; displayMasterPtr = true; break;

		case kVPBadlyFormattedPath:					errString = "Badly Formatted Path"; break;

		default:									errString = "REASON UNKNOWN"; break;
	}

	if (specialError) {
		switch (theErr.myErr) {
#if !_GSDEBUG_DLL_
			case kVPMemErrorReturnedError:	{

				long theMaster = 0;
				if (theData) {
					theMaster = *(long*)theData;
				}
				len = sprintf(reason, "MemError returned error %d\n   %s: (0x%.8lx)->(0x%.8lx)->data", theErr.sysErr, itemName, (Uint32) theData, theMaster);
				break;
			}
			case kVPHandleBlockTooSmall: {
				size_t theSize = GetHandleSize((Handle) theData);
				len = sprintf(reason, "Handle %s is too small\n   actual: %ld bytes  specified: %ld bytes", itemName, theSize, lCount);
				break;
			}
#endif
			case kVPPStringTooLong: {
				len = sprintf(reason, "String max size param (cbMax == %ld) too big", lCount);
				break;
			}
			case kVPStringMinSizeTooLong: {
				len = sprintf(reason, "String min size param (cbMin) too big ( > cbMax (%ld))", lCount);
				break;
			}
			case kVPStringBadLength: {
				len = sprintf(reason, "String length out of bounds (cbMin to cbMax (%ld))", lCount);
				break;
			}
			case kVPNegativeCount: {
				len = sprintf(reason, "Pointer size param (lCount = %ld) is negative", lCount);
				break;
			}

			default:
				len = sprintf(reason, "UNKNOWN ERROR");
		}
	}
	else if (displayMasterPtr) {
		long theMaster = 0;
		if (theData) {
			theMaster = *(long*)theData;
		}

		len = sprintf(reason, "%s\n   %s: (0x%.8lx)->(0x%.8lx)->data", errString, itemName, (Uint32) theData, theMaster);
	}
	else if (displayPtr) {
		len = sprintf(reason, "%s\n   %s: (0x%.8lx)->", errString, itemName, (Uint32) theData);
	}
	else {
		len = sprintf(reason, "%s", errString);
	}

	if (len > kBuf_size-1)
		DSTOP((kPaul, "_SendErrorMessage -  reason message is too big. IT IS CORRUPTING DATA !!!"));

	len = sprintf(buf, "%s(%s) failed!\n%s", theAssert, itemName, reason);
	if (len > kBuf_size-1)
		DSTOP((kPaul, "_SendErrorMessage -  buf message is too big. IT IS CORRUPTING DATA !!!"));
	return _FileDebugMessage(toWhom, file, line, buf);
}


//---------------------------------------------------------------------------------------
int _AssertValidPointer(const TProgrammer& toWhom, const char* file, int line, const char* itemName,
						const void *ptr, long lCount, Bool8 bReadOnly)
// Returns non-zero if breaking to debugger.
{
	if (toWhom.IsActLikeRelease())
		return 0;
	UserError theErr = IsValidAddress(ptr, lCount, bReadOnly);
	return _SendErrorMessage(toWhom, file, line, "AssertValidPointer", itemName, theErr, ptr, lCount);
}

//---------------------------------------------------------------------------------------
Bool8 _VerifyValidPointer(const TProgrammer& toWhom, const char* file, int line, const char* itemName,
						  const void *ptr, long lCount, Bool8 bReadOnly)
{
	if (toWhom.IsActLikeRelease())
		return ptr != NULL;
	UserError theErr = IsValidAddress(ptr, lCount, bReadOnly);
	if (_SendErrorMessage(toWhom, file, line, "VerifyValidPointer", itemName, theErr, ptr, lCount))
		STOP();
	return !theErr.IsError(true);
}

//---------------------------------------------------------------------------------------
int _AssertValidHandle(const TProgrammer& toWhom, const char* file, int line, const char* itemName, GSHandle h, long lCount)
{
	if (toWhom.IsActLikeRelease())
		return 0;
	UserError theErr = IsValidHandle(h, lCount, false);
	return _SendErrorMessage(toWhom, file, line, "AssertValidHandle", itemName, theErr, h, lCount);
}

//---------------------------------------------------------------------------------------
Bool8 _VerifyValidHandle(const TProgrammer& toWhom, const char* file, int line, const char* itemName, GSHandle h, long lCount)
{
	if (toWhom.IsActLikeRelease())
		return h != NULL;
	UserError theErr = IsValidHandle(h, lCount, false);
	if (_SendErrorMessage(toWhom, file, line, "VerifyValidHandle", itemName, theErr, h, lCount))
		STOP();
	return !theErr.IsError(true);
}

//---------------------------------------------------------------------------------------
int _AssertValidPString(Bool8 isAssert, const TProgrammer& toWhom, const char* file, int line,
						const char* itemName, ConstGSStringPtr ps, int cbMin, int cbMax)
{
	if (toWhom.IsActLikeRelease())
		return isAssert ? 0 : ps != NULL;
	UserError theErr = IsValidPString(ps, cbMin, cbMax);
	int ret = _SendErrorMessage(toWhom, file, line, "AssertValidPString", itemName, theErr, ps, cbMax);
	if (!isAssert) {
		if (ret)
			STOP();
		ret = !theErr.IsError(true);
	}
	return ret;
}

//---------------------------------------------------------------------------------------
int _AssertValidString(Bool8 isAssert, const TProgrammer& toWhom, const char* file, int line,
					   const char* itemName, const char *psz, long cbMin, long cbMax)
{
	if (toWhom.IsActLikeRelease())
		return isAssert ? 0 : psz != NULL;
	UserError theErr = IsValidString(psz, cbMin, cbMax);
	int ret = _SendErrorMessage(toWhom, file, line, "AssertValidString", itemName, theErr, psz, cbMax);
	if (!isAssert) {
		if (ret)
			STOP();
		ret = !theErr.IsError(true);
	}
	return ret;
}


#if _WINDOWS

//---------------------------------------------------------------------------------------
int GSDEBUG_EXPORT _AssertValidFilePath(Bool8 isAssert, const TProgrammer& toWhom, const char* file,
									  int line, const char* itemName, const char* fullPath)
{
	UserError theErr = IsValidString(fullPath, 3, MAX_PATH-1);
	if (theErr.myErr == kVPNoErr) {
		Bool8 validPath = false;

		validPath = ((fullPath[0] >= 'a' && fullPath[0] <= 'z') || (fullPath[0] >= 'A' && fullPath[0] <= 'Z')) &&
					fullPath[1] == ':' &&
					fullPath[2] == '\\';

		if (!validPath) {
			validPath = (fullPath[0] == '\\' && fullPath[1] == '\\') &&
						strchr(&fullPath[2], '\\');
		}

		if (!validPath)
			theErr.myErr = kVPBadlyFormattedPath;
	}

	int ret = _SendErrorMessage(toWhom, file, line, "AssertValidPath", itemName, theErr, fullPath, 0);
	if (!isAssert) {
		if (ret)
			STOP();
		ret = !theErr.IsError(true);
	}
	return ret;
}

#endif // _WINDOWS


#if !_WINDOWS

// this is a set of low level routine which walks the application heap manually. They must be updated
// if the memory manager changes

// call this on pointers or references passed to routines which may move memory
// this includes any routines which are not in Pre-loaded segments
// this should be used liberally

// (it only checks the application heap. If an object is in another heap, we will never assert!!)

enum {
	k24Heap = 1,
	k32Heap = 2,
	kModernHeap = 3
};

const unsigned char k24SizeCorrectionMask = 0x0F;

const unsigned char kBlockTypeMask = 0xC0;

const unsigned char kHandleBlock24 = 0x80;
const unsigned char kPtrBlock24 = 0x40;
const unsigned char kFreeBlock24 = 0x00;

const unsigned char kHandleBlock32 = 0x80;
const unsigned char kPtrBlock32 = 0x40;
const unsigned char kFreeBlock32 = 0x00;

const unsigned char kHandleBlockModern = 0x80;


typedef struct Header24 {
	unsigned char blockType;
	unsigned char highSize;
	unsigned short lowSize;
	void* zonePtr;
	unsigned char startData;
} *Header24Ptr;


typedef struct Header32 {
	unsigned char blockType;
	unsigned char masterPtrFlags;
	unsigned char reserved1;
	unsigned char sizeCorrection;
	size_t blockSize;
	void* zonePtr;
	unsigned char startData;
} *Header32Ptr;



static short GetHeapType(THz zone)
{
	static long gestaltResult = 0;
	static OSErr theErr = noErr;
	static bool bAddressingDetermined = false;

	if ( ! bAddressingDetermined) {
		theErr = Gestalt(gestaltAddressingModeAttr, &gestaltResult);
		bAddressingDetermined = true;
	}

	if (theErr == noErr) {
		if (gestaltResult & 1<<gestalt32BitAddressing) {
			if (zone->heapType & kNewStyleHeap)
				return kModernHeap;
			else
				return k32Heap;
		}
		else
			return k24Heap;
	}
	else
		return k24Heap;
}

static long GetZonePtr(Ptr blockPtr, short heapKind)
{
	switch (heapKind) {
		case k24Heap: {
			return (long)((Header24Ptr)blockPtr)->zonePtr;
		}
		case k32Heap:
		case kModernHeap: {
			return (long)((Header32Ptr)blockPtr)->zonePtr;
		}
		default: {
			DSTOP((kPaul, "PhysicalSize - Unknown heap type!!"));
			return 0L;
		}
	}
}


static size_t PhysicalSize(Ptr blockPtr, short heapKind)
{
	switch (heapKind) {
		case k24Heap: {
			return (size_t)((Header24Ptr)blockPtr)->lowSize + ((size_t)((Header24Ptr)blockPtr)->highSize * 0x00010000);
		}
		case k32Heap:
		case kModernHeap: {
			return ((Header32Ptr)blockPtr)->blockSize;
		}
		default: {
			DSTOP((kPaul, "PhysicalSize - Unknown heap type!!"));
			return 0L;
		}
	}
}

static size_t LogicalSize(Ptr blockPtr, short heapKind)
{
	switch (heapKind) {
		case k24Heap: {
			return (PhysicalSize(blockPtr, heapKind) - (((Header24Ptr)blockPtr)->blockType & k24SizeCorrectionMask));
		}
		case k32Heap:
		case kModernHeap: {
			return (PhysicalSize(blockPtr, heapKind) - (((Header32Ptr)blockPtr)->sizeCorrection ));
		}
		default: {
			DSTOP((kPaul, "LogicalSize - Unknown heap type!!"));
			return 0L;
		}
	}
}



static Ptr StartData(Ptr blockPtr, short heapKind)
{
	switch (heapKind) {
		case k24Heap: {
			return blockPtr + 8L;
		}
		case k32Heap:
		case kModernHeap: {
			return blockPtr + 12L;
		}
		default: {
			DSTOP((kPaul, "StartData - Unknown heap type!!"));
			return nil;
		}
	}
}

static Ptr EndData(Ptr blockPtr, short heapKind)
{
	return StartData(blockPtr, heapKind) + LogicalSize(blockPtr, heapKind);
}


static Bool8 IsContained(Ptr p, Ptr beginRange, Ptr endRange)
{
	return (p >= beginRange && p < endRange);
}


static Bool8 CheckBlockAddress(const TProgrammer& toWhom, const char* procName, Ptr blockPtr, THz /*heapZone - for future checks*/, short heapKind)
{
	toWhom;
	procName;

	Bool8 badAddress;

	if (heapKind == k24Heap)
		badAddress = ((unsigned long)blockPtr % 2 != 0);
	else
		badAddress = ((unsigned long)blockPtr % 4 != 0);

	if (badAddress) {
		DSTOP((toWhom, "CheckBlockAddress - Bad Block address when checking in %s- corrupt heap suspected!", procName));
	}

	return !badAddress;
}


static Bool8 IsHandle(Ptr blockPtr, short heapKind)
{
	switch (heapKind) {
		case k24Heap: {
			return (((Header24Ptr)blockPtr)->blockType & kBlockTypeMask) == kHandleBlock24;
		}
		case k32Heap:
		case kModernHeap: {
			return (((Header32Ptr)blockPtr)->blockType & kBlockTypeMask) == kHandleBlock32;
		}
//		case kModernHeap: {
//			return (((Header32Ptr)blockPtr)->blockType & kBlockTypeMask) == kHandleBlockModern;
//		}
		default: {
			DSTOP((kPaul, "IsHandle - Unknown heap type!!"));
			return false;
		}
	}
}

static Bool8 IsPointer(Ptr blockPtr, short heapKind)
{
	switch (heapKind) {
		case k24Heap: {
			return (((Header24Ptr)blockPtr)->blockType & kBlockTypeMask) == kPtrBlock24;
		}
		case k32Heap:
		case kModernHeap: {
			return (((Header32Ptr)blockPtr)->blockType & kBlockTypeMask) == kPtrBlock32;
		}
		default: {
			DSTOP((kPaul, "IsPointer - Unknown heap type!!"));
			return false;
		}
	}
}

static Bool8 IsFree(Ptr blockPtr, short heapKind)
{
	//DMSG((kPaul, "Block Type- raw: %x masked: %x\n", ((Header32Ptr)blockPtr)->blockType, ((Header32Ptr)blockPtr)->blockType & kBlockTypeMask));

	switch (heapKind) {
		case k24Heap: {
			return (((Header24Ptr)blockPtr)->blockType & kBlockTypeMask) == kFreeBlock24;
		}
		case k32Heap:
		case kModernHeap: {
			return (((Header32Ptr)blockPtr)->blockType & kBlockTypeMask) == kFreeBlock32;
		}
		default: {
			DSTOP((kPaul, "IsFree - Unknown heap type!!"));
			return false;
		}
	}
}


static Bool8 IsBlockBad(const TProgrammer& toWhom, Ptr blockPtr, Bool8 firstInHeap, THz heapZone, short heapKind)
{
	if (CheckBlockAddress(toWhom, "IsBlockBad", blockPtr, heapZone, heapKind)) {
		Bool8 weHavePointer = IsPointer(blockPtr, heapKind);
		// check if the pointer block's zone pointer points to our zone
		// we can't check the handle offset as easilt, but we may want to in the future
		if (!weHavePointer || (weHavePointer && GetZonePtr(blockPtr, heapKind) == (long)heapZone)) {
			if (heapKind == kModernHeap && !firstInHeap) {
				long physSize = PhysicalSize(blockPtr, heapKind);

				// the heap is terminated by a pointer block whose physical size is the size of the header
				// it has no data, padding, or back pointer
				if (!weHavePointer || (weHavePointer && physSize != 12)) {
					// all blocks must have a 12 byte header and at least 12 bytes of physical data
					if (physSize >= 24) {
						Bool8 weHaveFree = IsFree(blockPtr, heapKind);
						// only check the sizeCorrection if the block is not free - free blocks never have a correction
						if (weHaveFree || (!weHaveFree && ((Header32Ptr)blockPtr)->sizeCorrection >= 4)) {
							// the last four bytes of every block (included in the physical size, but not the logical size
							// contain a linked list of back pointers for reverse traversal of the heap. It's also
							// a great way to check block overwrites!

							if (*(unsigned long *)((unsigned long)blockPtr + physSize - 4L) == (unsigned long)blockPtr - 4L) {
								// now we know that the back pointer points to the word just before our block
								return false;
							}
							else {
								DSTOP((toWhom, "IsBlockBad corrupt heap - Checking block 0x%.8lx - Bad Back Pointer!", (long)blockPtr));
								return true;
							}
						}
						else {
							DSTOP((toWhom, "IsBlockBad corrupt heap - Checking block 0x%.8lx - size correction too small to acommodate back pointer!", (long)blockPtr));
							return true;
						}
					}
					else {
						DSTOP((toWhom, "IsBlockBad corrupt heap - Checking block 0x%.8lx - block is too small!", (long)blockPtr));
						return true;
					}
				}
				else {
					// this should be the last block in the heap
					if ((unsigned long)heapZone->bkLim != (unsigned long)blockPtr + physSize) {
						DSTOP((toWhom, "IsBlockBad corrupt heap - Checking block 0x%.8lx - thought this was the terminal block, but it's not!", (long)blockPtr));
					}
					else
						return false;
				}
			}
			else
				return false;
		}
		else {
			DSTOP((toWhom, "IsBlockBad corrupt heap - Checking block 0x%.8lx - pointer block zone pointer is bad!", (long)blockPtr));
			return true;
		}
	}
	return true;
}





// NOTE - the type of result is specified by the return value - kNoBlock, kHandle, kPointer, or kFree
// for free blocks, result is a pointer to the block header

short _FindContainingBlock(const TProgrammer& /*toWhom*/, void* /*ptr*/, long& /*result*/)
{
#if GS_PRECARBON
	Ptr p = (Ptr)ptr;

	short returnVal = kNoBlock;
	THz beginMyZone;

	Ptr currentBlock;
	Bool8 found = false;
	beginMyZone = ApplicationZone();

	short heapKind = GetHeapType(beginMyZone);


	if (IsContained(p, (Ptr)&beginMyZone->heapData, beginMyZone->bkLim)) {
		// p points to something in the application heap
		// we now walk the heap & determine what kind of block contains p
		//PRINT("Verifying heap block not volatile!!!");

		for (currentBlock = (Ptr)&beginMyZone->heapData; currentBlock < beginMyZone->bkLim; currentBlock += PhysicalSize(currentBlock, heapKind)) {

			CheckBlockAddress(toWhom, "FindContainingBlock", currentBlock, beginMyZone, heapKind);

			if (IsContained(p, StartData(currentBlock, heapKind), EndData(currentBlock, heapKind))) {

				//PRINT("Found block containing pointer!");
				// p points to data inside currentBlock!!

				if (IsHandle(currentBlock, heapKind)) {
					//PRINT("We're in a handle!");
					Handle containingHandle;
					containingHandle = (Handle)((long)beginMyZone + GetZonePtr(currentBlock, heapKind));

					result = (long)containingHandle;
					returnVal = kHandle;
				}
				else if (IsPointer(currentBlock, heapKind)) {
					result = (long)StartData(currentBlock, heapKind);
					returnVal = kPointer;
				}
				else if (IsFree(currentBlock, heapKind)) {
					result = (long)currentBlock;
					returnVal = kFree;
				}
				else {
					DSTOP((toWhom, "FindContainingBlock - Block containing %lx found in heap type %d, but was not Handle, pointer, or free!!\n", (long)p, heapKind));
				}
				found = true;
				break;
			}
		}

		DSTOP((!found, toWhom, "FindContainingBlock - Containing block not found - suspect bad heap!!\n"));
	}
#else
	short returnVal = kNoBlock;
	//DSTOP ((kVictor, "XXX:Carbon - ApplicationZone not available in Carbon.\n"));
#endif
	return returnVal;
}



int _AssertNotVolatile(const TProgrammer& /*toWhom*/, const char* /*file*/, int /*line*/, const char* /*ptrName*/, void* /*ptr*/)
{
	#if GS_PRECARBON
	long result;
	Ptr thePtr = (Ptr) ptr;
	short containing = _FindContainingBlock(toWhom, thePtr, result);

	if (containing == kHandle) {
		Handle containingHandle = (Handle)result;
		char test = HGetState(containingHandle);
		DSTOP((!(test & 0x80), toWhom, "Volatile Pointer '%s' (0x%.8lx) - points to unlocked handle\n   file %s, line %d!", ptrName, thePtr, file, line));
	}
	else if (containing == kFree) {
		DSTOP((toWhom, "Volatile Pointer '%s' (0x%.8lx) - points to free block\n   file %s, line %d!", ptrName, thePtr, file, line));
	}
	else if (containing == kNoBlock) {
		DSTOP((toWhom, "Volatile Pointer '%s' (0x%.8lx) - does not point to block\n   file %s, line %d!", ptrName, thePtr, file, line));
	}
	#endif
	return 0;
}


#endif // !_WINDOWS



// call CheckHeap any time memory ops are legal. It checks that the heap is valid & checks
// available block sizes & fragmentation.

void GSDEBUG_EXPORT CheckHeap(const TProgrammer &WIN_ONLY(toWhom))
{
#if _WINDOWS
	#if BUG
		#if _GSDEBUG_DLL_ || _GSESDK_
			HANDLE heaps[40];
			const DWORD nHeaps = GetProcessHeaps(40, heaps);
			DSTOP((nHeaps > 40, toWhom, "Cannot check heaps because too many"));
			for (DWORD i = 0; i < nHeaps; ++i)
				DSTOP((!HeapValidate(heaps[i], 0, NULL), toWhom, "Invalid heap found!!"));
		#else
			// Check the C run-time heap.
			DSTOPIF(!_CrtCheckMemory(), (toWhom, "C run-time heap is not valid. Check "
				"debug output window for more info. Use _CRTDBG_CHECK_ALWAYS_DF flag "
				"in InitDebugCRT() to help locate problem."));

			// Check Altura's heaps.
// Dec 19, 2001 AlturaRemoval, jg
//			ASI_MemCheck();
		#endif
	#endif // BUG

#else
#if GS_PRECARBON
	THz beginMyZone;
	Ptr endMyZone;

	Ptr currentBlock;
	size_t thisSize;

	// heap stats
	long maxFreeSize = 0L;

	long free100Count = 0L;
	long free500Count = 0L;
	long free1000Count = 0L;
	long free2000Count = 0L;

	long freeCount = 0L;
	size_t freeSizeTot = 0L;
	long ptrCount = 0L;
	size_t ptrSizeTot = 0L;
	long handleCount = 0L;
	size_t handleSizeTot = 0L;


	Bool8 found = false;
	beginMyZone = ApplicationZone();
	endMyZone = GetApplLimit();

	short heapKind = GetHeapType(beginMyZone);

	#ifdef Paul
	//DMSG((kEveryone, "Heap Type: %d\n", heapKind));
	#endif

	Bool8 firstInHeap;
	for (currentBlock = (Ptr)&beginMyZone->heapData, firstInHeap = true; currentBlock < beginMyZone->bkLim; currentBlock += PhysicalSize(currentBlock, heapKind), firstInHeap = false) {
		if (!IsBlockBad(toWhom, currentBlock, firstInHeap, beginMyZone, heapKind)) {
			thisSize = PhysicalSize(currentBlock, heapKind);

			#ifdef Paul
			//DMSG((kEveryone, "Examining block at %lx of size %ld bytes\n", (long)currentBlock, thisSize));
			#endif

			if (IsFree(currentBlock, heapKind)) {
				freeCount++;
				freeSizeTot += thisSize;
				if (thisSize > maxFreeSize)
					maxFreeSize = thisSize;
				if (thisSize <= 100L)
					free100Count++;
				else if (thisSize <= 500L)
					free500Count++;
				else if (thisSize <= 1000L)
					free1000Count++;
				else if (thisSize <= 2000L)
					free2000Count++;
			}
			else if (IsPointer(currentBlock, heapKind)) {
				ptrCount++;
				ptrSizeTot += PhysicalSize(currentBlock, heapKind);
			}
			else if (IsHandle(currentBlock, heapKind)) {
				handleCount++;
				handleSizeTot += PhysicalSize(currentBlock, heapKind);
			}
		}
		else
			return; // bail out because we already know the heap is bad
	}

	#if _WINDOWS
	#else
	// last block header may have been overwritten by the stack
	if (currentBlock > beginMyZone->bkLim) {
		DSTOP((toWhom, "Corrupt heap - currentBlock > beginMyZone->bkLim"));
	}
	#endif

	#if Paul
	DMSG((kEveryone, "====Total Handles:    %10.10d accounting for %10.10d bytes\n", handleCount, handleSizeTot));
	DMSG((kEveryone, "====Total Pointers:   %10.10d accounting for %10.10d bytes\n", ptrCount, ptrSizeTot));
	if (freeCount > 1000 || maxFreeSize < 20000 || maxFreeSize < freeSizeTot / 4) {
		DMSG((kEveryone, "========Fragmentation ALERT========\n"));
		DMSG((kEveryone, "Free Block Count: %ld\n", freeCount));
		DMSG((kEveryone, "Free size: %ld\n", freeSizeTot));
		DMSG((kEveryone, "Max free block: %ld\n", maxFreeSize));
		DMSG((kEveryone, "----------Fragmentation------------\n"));
		DMSG((kEveryone, "0-100    : %ld\n", free100Count));
		DMSG((kEveryone, "100-500  : %ld\n", free500Count));
		DMSG((kEveryone, "500-1000 : %ld\n", free1000Count));
		DMSG((kEveryone, "1000-2000: %ld\n", free2000Count));
		DMSG((kEveryone, ">2000    : %ld\n", freeCount - free100Count - free500Count - free1000Count - free2000Count));
	}
	#endif

	if (freeCount > 10000 || maxFreeSize < 10000 || maxFreeSize < freeSizeTot / 12) {
		char buf[512];
		sprintf(buf, "HeapCheck - Fragmented heap or VERY low memory   Free Blocks: %ld  Largest Free: %ld   Total Bytes Free: %ld", freeCount, maxFreeSize, freeSizeTot);

		#ifndef BUG
		DSTOP((MAKEWHO("HeapCheck"), "%s", buf));
		#else
		#if NETBUG
		RemoteDebug("HeapCheck", buf);
		#endif
		#endif
	}
#else
	//DSTOP ((kVictor, "XXX:Carbon - ApplicationZone and GetApplLimit not available in Carbon.\n"));
#endif
#endif
}

#endif // _MINICAD_EXTERNAL_



#if !_WINDOWS

Bool8 __IsHandleLocked(GSHandle h)
{
	return (HGetState(h) & 0x80) ? true : false;
}

#endif


#endif // TEST



/////////////////////////////////////////////////////////////////////////////////////////
// TDebugObject

//---------------------------------------------------------------------------------------
#if TEST
void TDebugObject::AssertValid(const TProgrammer &toWhom, Bool8 bCheckMem) const
{
	if (bCheckMem)
		ASSERTPOINTER(toWhom, this, 1);
}
#endif // TEST

//---------------------------------------------------------------------------------------
#if BUG && GS_USE_CLASS_DEBUGGING
void TDebugObject::Dump(const TProgrammer &toWhom, TDumpDevice &dd, int indent) const
{
	dd.printf(toWhom, indent, "----------------------------------------\n"
							  "TDebugObject derived class at $%08x\n", this);
}
#endif // BUG && GS_USE_CLASS_DEBUGGING

/////////////////////////////////////////////////////////////////////////////////////////
// TVirtualDebugObject

//---------------------------------------------------------------------------------------
#if TEST && GS_USE_CLASS_DEBUGGING
void TVirtualDebugObject::AssertValid(const TProgrammer &toWhom, Bool8 bCheckMem) const
{
	if (bCheckMem)
		ASSERTPOINTER(toWhom, this, 1);
}
#endif // TEST

//---------------------------------------------------------------------------------------
#if BUG && GS_USE_CLASS_DEBUGGING
void TVirtualDebugObject::Dump(const TProgrammer &toWhom, TDumpDevice &dd, int indent) const
{
	dd.printf(toWhom, indent, "----------------------------------------\n"
							  "TVirtualDebugObject derived class at $%08x\n", this);
}
#endif // BUG && GS_USE_CLASS_DEBUGGING


#if _WINDOWS
/////////////////////////////////////////////////////////////////////////////////////////
// TWinErrorCode

//---------------------------------------------------------------------------------------
TWinErrorCode::TWinErrorCode()
{
	// Call GetLastError() and use FormatMessage() to get the message string
	// for the error
	::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
					NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
					(LPTSTR) &fMessageBuffer, 0, NULL);

	// strip carriage return, linefeed from message string
	char *string = (char *)fMessageBuffer;

	int n = strlen(string)-1;
	while (string[n] == '\n' || string[n] == '\r') {
		string[n] = '\0';
		n--;
	}
}

TWinErrorCode::~TWinErrorCode()
{
	::LocalFree(fMessageBuffer);
}
#endif	// _WINDOWS


/////////////////////////////////////////////////////////////////////////////////////////
// Timers

#if BUG
	#if _WINDOWS
		// Windows implementation.

		TDebugTimer::TDebugTimer()
		{
			ASSERTN(kDave, sizeof(LARGE_INTEGER) == sizeof(fStartTime));
			QueryPerformanceCounter((PLARGE_INTEGER)&fStartTime);
		}

		void TDebugTimer::Stop(const TProgrammer &who, ConstGSCStrPtr name, bool bWindow)
		{
			// Get end time.
			Uint64 stopTime;
			if (QueryPerformanceCounter((PLARGE_INTEGER)&stopTime)) {

				// Get number of units per second in the timer.
				Sint64 perSecond;
				if (QueryPerformanceFrequency((PLARGE_INTEGER)&perSecond)) {

					// Compute seconds.
					const double seconds = (Sint64)(stopTime - fStartTime) / (double)perSecond;

					// Display results.
					char szMessage[256];
					sprintf(szMessage, "Timer '%s' took %.3f seconds.\n", name, seconds);
					DMSG((who, szMessage));
					if (bWindow)
						::MessageBox(g_hParentWnd, szMessage, "Timer Results", MB_OK | MB_ICONINFORMATION);
					return;
				}
			}
			DMSG((who, "Performance counters not supported by hardware.\n"));
		}

	#else
		// Mac implementaion.

	#endif // _WINDOWS
#endif // BUG
#endif //defined(GS_MAC) || defined(DEV_BLD) - Added to create bogus release version of GSDebug lib. [VML 01/09/04]
