/* -*- Mode: C; tab-width: 4 -*-
 *
 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.

	To Do:
	
	- Use StackWalk on Windows to optionally print stack frames.
*/

#if 0
#pragma mark == Includes ==
#endif

//===========================================================================================================================
//	Includes
//===========================================================================================================================

#if( !KERNEL )
	#include	<ctype.h>
	#include	<stdio.h>
	#include	<string.h>
#endif

#include	"CommonServices.h"

#include	"DebugServices.h"

#if( DEBUG )

#if( TARGET_OS_VXWORKS )
	#include	"intLib.h"
#endif

#if( TARGET_OS_WIN32 )
	#include	<time.h>
	
	#if( !TARGET_OS_WINDOWS_CE )
		#include	<fcntl.h>
		#include	<io.h>
	#endif
#endif

#if( DEBUG_IDEBUG_ENABLED && TARGET_API_MAC_OSX_KERNEL )
	#include	<IOKit/IOLib.h>
#endif

// If MDNS_DEBUGMSGS is defined (even if defined 0), it is aware of mDNS and it is probably safe to include mDNSEmbeddedAPI.h.

#if( defined( MDNS_DEBUGMSGS ) )
	#include	"mDNSEmbeddedAPI.h"
#endif

#if 0
#pragma mark == Macros ==
#endif

//===========================================================================================================================
//	Macros
//===========================================================================================================================

#define DebugIsPrint( C )		( ( ( C ) >= 0x20 ) && ( ( C ) <= 0x7E ) )

#if 0
#pragma mark == Prototypes ==
#endif

//===========================================================================================================================
//	Prototypes
//===========================================================================================================================

static OSStatus	DebugPrint( DebugLevel inLevel, char *inData, size_t inSize );

// fprintf

#if( DEBUG_FPRINTF_ENABLED )
	static OSStatus	DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename );
	static void		DebugFPrintFPrint( char *inData, size_t inSize );
#endif

// iDebug (Mac OS X user and kernel)

#if( DEBUG_IDEBUG_ENABLED )
	static OSStatus	DebugiDebugInit( void );
	static void		DebugiDebugPrint( char *inData, size_t inSize );
#endif

// kprintf (Mac OS X Kernel)

#if( DEBUG_KPRINTF_ENABLED )
	static void	DebugKPrintFPrint( char *inData, size_t inSize );
#endif

// Mac OS X IOLog (Mac OS X Kernel)

#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
	static void	DebugMacOSXIOLogPrint( char *inData, size_t inSize );
#endif

// Mac OS X Log

#if( TARGET_OS_MAC )
	static OSStatus	DebugMacOSXLogInit( void );
	static void		DebugMacOSXLogPrint( char *inData, size_t inSize );
#endif

// Windows Debugger

#if( TARGET_OS_WIN32 )
	static void	DebugWindowsDebuggerPrint( char *inData, size_t inSize );
#endif

// Windows Event Log

#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
	static OSStatus	DebugWindowsEventLogInit( const char *inName, HMODULE inModule );
	static void	DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize );
#endif

// DebugLib support

#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
	static pascal void	
		DebugAssertOutputHandler( 
			OSType 				inComponentSignature, 
			UInt32 				inOptions, 
			const char *		inAssertionString, 
			const char *		inExceptionString, 
			const char *		inErrorString, 
			const char *		inFileName, 
			long 				inLineNumber, 
			void *				inValue, 
			ConstStr255Param 	inOutputMsg );
#endif

// Utilities

static char *	DebugNumVersionToString( uint32_t inVersion, char *inString );

#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
	static void	DebugWinEnableConsole( void );
#endif

#if( TARGET_OS_WIN32 )
	static TCHAR *
		DebugWinCharToTCharString( 
			const char *	inCharString, 
			size_t 			inCharCount, 
			TCHAR *			outTCharString, 
			size_t 			inTCharCountMax, 
			size_t *		outTCharCount );
#endif

#if 0
#pragma mark == Globals ==
#endif

//===========================================================================================================================
//	Private Globals
//===========================================================================================================================

#if( TARGET_OS_VXWORKS )
	// TCP States for inetstatShow.

	extern char **	pTcpstates;		// defined in tcpLib.c

	const char *		kDebugTCPStates[] =
	{
		"(0)  TCPS_CLOSED", 
		"(1)  TCPS_LISTEN", 
		"(2)  TCPS_SYN_SENT", 
		"(3)  TCPS_SYN_RECEIVED", 
		"(4)  TCPS_ESTABLISHED", 
		"(5)  TCPS_CLOSE_WAIT", 
		"(6)  TCPS_FIN_WAIT_1", 
		"(7)  TCPS_CLOSING", 
		"(8)  TCPS_LAST_ACK", 
		"(9)  TCPS_FIN_WAIT_2", 
		"(10) TCPS_TIME_WAIT",
	};
#endif

// General

static bool									gDebugInitialized				= false;
static DebugOutputType						gDebugOutputType 				= kDebugOutputTypeNone;
static DebugLevel							gDebugPrintLevelMin				= kDebugLevelInfo;
static DebugLevel							gDebugPrintLevelMax				= kDebugLevelMax;
static DebugLevel							gDebugBreakLevel				= kDebugLevelAssert;
#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
	static DebugAssertOutputHandlerUPP		gDebugAssertOutputHandlerUPP	= NULL;
#endif

// Custom

static DebugOutputFunctionPtr				gDebugCustomOutputFunction 		= NULL;
static void *								gDebugCustomOutputContext 		= NULL;

// fprintf

#if( DEBUG_FPRINTF_ENABLED )
	static FILE *							gDebugFPrintFFile 				= NULL;
#endif

// MacOSXLog

#if( TARGET_OS_MAC )
	typedef int	( *DebugMacOSXLogFunctionPtr )( const char *inFormat, ... );
	
	static DebugMacOSXLogFunctionPtr		gDebugMacOSXLogFunction			= NULL;
#endif

// WindowsEventLog


#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
	static HANDLE							gDebugWindowsEventLogEventSource = NULL;
#endif

#if 0
#pragma mark -
#pragma mark == General ==
#endif

//===========================================================================================================================
//	DebugInitialize
//===========================================================================================================================

DEBUG_EXPORT OSStatus	DebugInitialize( DebugOutputType inType, ... )
{
	OSStatus			err;
	DebugOutputType		type;
	va_list				args;
	
	va_start( args, inType );

#if( TARGET_OS_VXWORKS )
	// Set up the TCP state strings if they are not already set up by VxWorks (normally not set up for some reason).
	
	if( !pTcpstates )
	{
		pTcpstates = (char **) kDebugTCPStates;
	}
#endif
	
	// Set up DebugLib stuff (if building with Debugging.h).
	
#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
	if( !gDebugAssertOutputHandlerUPP )
	{
		gDebugAssertOutputHandlerUPP = NewDebugAssertOutputHandlerUPP( DebugAssertOutputHandler );
		check( gDebugAssertOutputHandlerUPP );
		if( gDebugAssertOutputHandlerUPP )
		{
			InstallDebugAssertOutputHandler( gDebugAssertOutputHandlerUPP );
		}
	}
#endif
	
	// Pre-process meta-output kind to pick an appropriate output kind for the platform.
	
	type = inType;
	if( type == kDebugOutputTypeMetaConsole )
	{
		#if( TARGET_OS_MAC )
			type = kDebugOutputTypeMacOSXLog;
		#elif( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
			#if( DEBUG_FPRINTF_ENABLED )
				type = kDebugOutputTypeFPrintF;
			#else
				type = kDebugOutputTypeWindowsDebugger;
			#endif
		#elif( TARGET_API_MAC_OSX_KERNEL )
			#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
				type = kDebugOutputTypeMacOSXIOLog;
			#elif( DEBUG_IDEBUG_ENABLED )
				type = kDebugOutputTypeiDebug;
			#elif( DEBUG_KPRINTF_ENABLED )
				type = kDebugOutputTypeKPrintF;
			#endif
		#elif( TARGET_OS_VXWORKS )
			#if( DEBUG_FPRINTF_ENABLED )
				type = kDebugOutputTypeFPrintF;
			#else
				#error target is VxWorks, but fprintf output is disabled
			#endif
		#else
			#if( DEBUG_FPRINTF_ENABLED )
				type = kDebugOutputTypeFPrintF;
			#endif
		#endif
	}
	
	// Process output kind.
	
	gDebugOutputType = type;
	switch( type )
	{
		case kDebugOutputTypeNone:
			err = kNoErr;
			break;

		case kDebugOutputTypeCustom:
			gDebugCustomOutputFunction = va_arg( args, DebugOutputFunctionPtr );
			gDebugCustomOutputContext  = va_arg( args, void * );
			err = kNoErr;
			break;

#if( DEBUG_FPRINTF_ENABLED )
		case kDebugOutputTypeFPrintF:
			if( inType == kDebugOutputTypeMetaConsole )
			{
				err = DebugFPrintFInit( kDebugOutputTypeFlagsStdErr, NULL );
			}
			else
			{
				DebugOutputTypeFlags		flags;
				const char *				filename;
				
				flags = (DebugOutputTypeFlags) va_arg( args, unsigned int );
				if( ( flags & kDebugOutputTypeFlagsTypeMask ) == kDebugOutputTypeFlagsFile )
				{
					filename = va_arg( args, const char * );
				}
				else
				{
					filename = NULL;
				}
				err = DebugFPrintFInit( flags, filename );
			}
			break;
#endif

#if( DEBUG_IDEBUG_ENABLED )
		case kDebugOutputTypeiDebug:
			err = DebugiDebugInit();
			break;
#endif

#if( DEBUG_KPRINTF_ENABLED )
		case kDebugOutputTypeKPrintF:
			err = kNoErr;
			break;
#endif

#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
		case kDebugOutputTypeMacOSXIOLog:
			err = kNoErr;
			break;
#endif

#if( TARGET_OS_MAC )
		case kDebugOutputTypeMacOSXLog:
			err = DebugMacOSXLogInit();
			break;
#endif

#if( TARGET_OS_WIN32 )
		case kDebugOutputTypeWindowsDebugger:
			err = kNoErr;
			break;
#endif

#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
		case kDebugOutputTypeWindowsEventLog:
		{
			const char *		name;
			HMODULE				module;
			
			name   = va_arg( args, const char * );
			module = va_arg( args, HMODULE );
			err = DebugWindowsEventLogInit( name, module );
		}
		break;
#endif

		default:
			err = kParamErr;
			goto exit;
	}
	gDebugInitialized = true;
	
exit:
	va_end( args );
	return( err );
}

//===========================================================================================================================
//	DebugFinalize
//===========================================================================================================================

DEBUG_EXPORT void		DebugFinalize( void )
{
#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
	check( gDebugAssertOutputHandlerUPP );
	if( gDebugAssertOutputHandlerUPP )
	{
		InstallDebugAssertOutputHandler( NULL );
		DisposeDebugAssertOutputHandlerUPP( gDebugAssertOutputHandlerUPP );
		gDebugAssertOutputHandlerUPP = NULL;
	}
#endif
}

//===========================================================================================================================
//	DebugGetProperty
//===========================================================================================================================

DEBUG_EXPORT OSStatus	DebugGetProperty( DebugPropertyTag inTag, ... )
{
	OSStatus			err;
	va_list				args;
	DebugLevel *		level;
	
	va_start( args, inTag );
	switch( inTag )
	{
		case kDebugPropertyTagPrintLevelMin:
			level  = va_arg( args, DebugLevel * );
			*level = gDebugPrintLevelMin;
			err = kNoErr;
			break;
		
		case kDebugPropertyTagPrintLevelMax:
			level  = va_arg( args, DebugLevel * );
			*level = gDebugPrintLevelMax;
			err = kNoErr;
			break;
		
		case kDebugPropertyTagBreakLevel:
			level  = va_arg( args, DebugLevel * );
			*level = gDebugBreakLevel;
			err = kNoErr;
			break;		
		
		default:
			err = kUnsupportedErr;
			break;
	}
	va_end( args );
	return( err );
}

//===========================================================================================================================
//	DebugSetProperty
//===========================================================================================================================

DEBUG_EXPORT OSStatus	DebugSetProperty( DebugPropertyTag inTag, ... )
{
	OSStatus		err;
	va_list			args;
	DebugLevel		level;
	
	va_start( args, inTag );
	switch( inTag )
	{
		case kDebugPropertyTagPrintLevelMin:
			level  = va_arg( args, DebugLevel );
			gDebugPrintLevelMin = level;
			err = kNoErr;
			break;
		
		case kDebugPropertyTagPrintLevelMax:
			level  = va_arg( args, DebugLevel );
			gDebugPrintLevelMax = level;
			err = kNoErr;
			break;
		
		case kDebugPropertyTagBreakLevel:
			level  = va_arg( args, DebugLevel );
			gDebugBreakLevel = level;
			err = kNoErr;
			break;		
		
		default:
			err = kUnsupportedErr;
			break;
	}
	va_end( args );
	return( err );
}

#if 0
#pragma mark -
#pragma mark == Output ==
#endif

//===========================================================================================================================
//	DebugPrintF
//===========================================================================================================================

DEBUG_EXPORT size_t	DebugPrintF( DebugLevel inLevel, const char *inFormat, ... )
{	
	va_list		args;
	size_t		n;
	
	// Skip if the level is not in the enabled range..
	
	if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
	{
		n = 0;
		goto exit;
	}
	
	va_start( args, inFormat );
	n = DebugPrintFVAList( inLevel, inFormat, args );
	va_end( args );

exit:
	return( n );
}

//===========================================================================================================================
//	DebugPrintFVAList
//===========================================================================================================================

DEBUG_EXPORT size_t	DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs )
{
	size_t		n;
	char		buffer[ 512 ];
	
	// Skip if the level is not in the enabled range..
	
	if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
	{
		n = 0;
		goto exit;
	}
	
	n = DebugSNPrintFVAList( buffer, sizeof( buffer ), inFormat, inArgs );
	DebugPrint( inLevel, buffer, (size_t) n );

exit:
	return( n );
}

//===========================================================================================================================
//	DebugPrint
//===========================================================================================================================

static OSStatus	DebugPrint( DebugLevel inLevel, char *inData, size_t inSize )
{
	OSStatus		err;
	
	// Skip if the level is not in the enabled range..
	
	if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
	{
		err = kRangeErr;
		goto exit;
	}
	
	// Printing is not safe at interrupt time so check for this and warn with an interrupt safe mechanism (if available).
	
	if( DebugTaskLevel() & kDebugInterruptLevelMask )
	{
		#if( TARGET_OS_VXWORKS )
			logMsg( "\ncannot print at interrupt time\n\n", 1, 2, 3, 4, 5, 6 );
		#endif
		
		err = kExecutionStateErr;
		goto exit;
	}
	
	// Initialize the debugging library if it hasn't already been initialized (allows for zero-config usage).
	
	if( !gDebugInitialized )
	{
		debug_initialize( kDebugOutputTypeMetaConsole );
	}
	
	// Print based on the current output type.
	
	switch( gDebugOutputType )
	{
		case kDebugOutputTypeNone:
			break;
		
		case kDebugOutputTypeCustom:
			if( gDebugCustomOutputFunction )
			{
				gDebugCustomOutputFunction( inData, inSize, gDebugCustomOutputContext );
			}
			break;

#if( DEBUG_FPRINTF_ENABLED )
		case kDebugOutputTypeFPrintF:
			DebugFPrintFPrint( inData, inSize );
			break;
#endif

#if( DEBUG_IDEBUG_ENABLED )
		case kDebugOutputTypeiDebug:
			DebugiDebugPrint( inData, inSize );
			break;
#endif

#if( DEBUG_KPRINTF_ENABLED )
		case kDebugOutputTypeKPrintF:
			DebugKPrintFPrint( inData, inSize );
			break;
#endif

#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
		case kDebugOutputTypeMacOSXIOLog:
			DebugMacOSXIOLogPrint( inData, inSize );
			break;
#endif

#if( TARGET_OS_MAC )
		case kDebugOutputTypeMacOSXLog:
			DebugMacOSXLogPrint( inData, inSize );
			break;
#endif

#if( TARGET_OS_WIN32 )
		case kDebugOutputTypeWindowsDebugger:
			DebugWindowsDebuggerPrint( inData, inSize );
			break;
#endif

#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
		case kDebugOutputTypeWindowsEventLog:
			DebugWindowsEventLogPrint( inLevel, inData, inSize );
			break;
#endif

		default:
			break;
	}
	err = kNoErr;
	
exit:
	return( err );
}

//===========================================================================================================================
//	DebugPrintAssert
//
//	Warning: This routine relies on several of the strings being string constants that will exist forever because the
//           underlying logMsg API that does the printing is asynchronous so it cannot use temporary/stack-based 
//           pointer variables (e.g. local strings). The debug macros that invoke this function only use constant 
//           constant strings, but if this function is invoked directly from other places, it must use constant strings.
//===========================================================================================================================

DEBUG_EXPORT void
	DebugPrintAssert( 
		int_least32_t	inErrorCode, 
		const char *	inAssertString, 
		const char *	inMessage, 
		const char *	inFilename, 
		int_least32_t	inLineNumber, 
		const char *	inFunction )
{
	// Skip if the level is not in the enabled range..
	
	if( ( kDebugLevelAssert < gDebugPrintLevelMin ) || ( kDebugLevelAssert > gDebugPrintLevelMax ) )
	{
		return;
	}
	
	if( inErrorCode != 0 )
	{
		DebugPrintF( 
			kDebugLevelAssert, 
			"\n"
			"[ASSERT] error:  %ld (%m)\n"
			"[ASSERT] where:  \"%s\", line %ld, \"%s\"\n"
			"\n", 
			inErrorCode, inErrorCode, 
			inFilename ? inFilename : "", 
			inLineNumber, 
			inFunction ? inFunction : "" );
	}
	else
	{
		DebugPrintF( 
			kDebugLevelAssert, 
			"\n"
			"[ASSERT] assert: \"%s\" %s\n"
			"[ASSERT] where:  \"%s\", line %ld, \"%s\"\n"
			"\n", 
			inAssertString ? inAssertString : "", 
			inMessage ? inMessage : "", 
			inFilename ? inFilename : "", 
			inLineNumber, 
			inFunction ? inFunction : "" );
	}
	
	// Break into the debugger if enabled.
	
	#if( TARGET_OS_WIN32 )
		if( gDebugBreakLevel <= kDebugLevelAssert )
		{
			if( IsDebuggerPresent() )
			{
				DebugBreak();
			}
		}
	#endif
}

#if 0
#pragma mark -
#endif

#if( DEBUG_FPRINTF_ENABLED )
//===========================================================================================================================
//	DebugFPrintFInit
//===========================================================================================================================

static OSStatus	DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename )
{
	OSStatus					err;
	DebugOutputTypeFlags		typeFlags;
	
	typeFlags = inFlags & kDebugOutputTypeFlagsTypeMask;
	if( typeFlags == kDebugOutputTypeFlagsStdOut )
	{
		#if( TARGET_OS_WIN32 )
			DebugWinEnableConsole();
		#endif

		gDebugFPrintFFile = stdout;
	}
	else if( typeFlags == kDebugOutputTypeFlagsStdErr )
	{
		#if( TARGET_OS_WIN32 )
			DebugWinEnableConsole();
		#endif
		
		gDebugFPrintFFile = stdout;
	}
	else if( typeFlags == kDebugOutputTypeFlagsFile )
	{
		require_action_quiet( inFilename && ( *inFilename != '\0' ), exit, err = kOpenErr );
		
		gDebugFPrintFFile = fopen( inFilename, "a" );
		require_action_quiet( gDebugFPrintFFile, exit, err = kOpenErr );
	}
	else
	{
		err = kParamErr;
		goto exit;
	}
	err = kNoErr;
	
exit:
	return( err );
}

//===========================================================================================================================
//	DebugFPrintFPrint
//===========================================================================================================================

static void	DebugFPrintFPrint( char *inData, size_t inSize )
{
	char *		p;
	char *		q;
	
	// Convert \r to \n. fprintf will interpret \n and convert to whatever is appropriate for the platform.
	
	p = inData;
	q = p + inSize;
	while( p < q )
	{
		if( *p == '\r' )
		{
			*p = '\n';
		}
		++p;
	}
	
	// Write the data and flush.
	
	if( gDebugFPrintFFile )
	{
		fprintf( gDebugFPrintFFile, "%.*s", (int) inSize, inData );
		fflush( gDebugFPrintFFile );
	}
}
#endif	// DEBUG_FPRINTF_ENABLED

#if( DEBUG_IDEBUG_ENABLED )
//===========================================================================================================================
//	DebugiDebugInit
//===========================================================================================================================

static OSStatus	DebugiDebugInit( void )
{
	OSStatus		err;
	
	#if( TARGET_API_MAC_OSX_KERNEL )
		
		extern uint32_t *		_giDebugReserved1;
		
		// Emulate the iDebugSetOutputType macro in iDebugServices.h.
		// Note: This is not thread safe, but neither is iDebugServices.h nor iDebugKext.
		
		if( !_giDebugReserved1 )
		{
			_giDebugReserved1 = (uint32_t *) IOMalloc( sizeof( uint32_t ) );
			require_action_quiet( _giDebugReserved1, exit, err = kNoMemoryErr );
		}
		*_giDebugReserved1 = 0x00010000U;
		err = kNoErr;
exit:
	#else
		
		__private_extern__ void	iDebugSetOutputTypeInternal( uint32_t inType );
		
		iDebugSetOutputTypeInternal( 0x00010000U );
		err = kNoErr;
		
	#endif
	
	return( err );
}

//===========================================================================================================================
//	DebugiDebugPrint
//===========================================================================================================================

static void	DebugiDebugPrint( char *inData, size_t inSize )
{
	#if( TARGET_API_MAC_OSX_KERNEL )
		
		// Locally declared here so we do not need to include iDebugKext.h.
		// Note: IOKit uses a global namespace for all code and only a partial link occurs at build time. When the 
		// KEXT is loaded, the runtime linker will link in this extern'd symbol (assuming iDebug is present).
		// _giDebugLogInternal is actually part of IOKit proper so this should link even if iDebug is not present.
		
		typedef void ( *iDebugLogFunctionPtr )( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... );
		
		extern iDebugLogFunctionPtr		_giDebugLogInternal;
		
		if( _giDebugLogInternal )
		{
			_giDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData );
		}
		
	#else
	
		__private_extern__ void	iDebugLogInternal( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... );
		
		iDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData );
	
	#endif
}
#endif

#if( DEBUG_KPRINTF_ENABLED )
//===========================================================================================================================
//	DebugKPrintFPrint
//===========================================================================================================================

static void	DebugKPrintFPrint( char *inData, size_t inSize )
{
	extern void	kprintf( const char *inFormat, ... );
	
	kprintf( "%.*s", (int) inSize, inData );
}
#endif

#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
//===========================================================================================================================
//	DebugMacOSXIOLogPrint
//===========================================================================================================================

static void	DebugMacOSXIOLogPrint( char *inData, size_t inSize )
{
	extern void	IOLog( const char *inFormat, ... );
	
	IOLog( "%.*s", (int) inSize, inData );
}
#endif

#if( TARGET_OS_MAC )
//===========================================================================================================================
//	DebugMacOSXLogInit
//===========================================================================================================================

static OSStatus	DebugMacOSXLogInit( void )
{
	OSStatus		err;
	CFStringRef		path;
	CFURLRef		url;
	CFBundleRef		bundle;
	CFStringRef		functionName;
	void *			functionPtr;
	
	bundle = NULL;
	
	// Create a bundle reference for System.framework.
	
	path = CFSTR( "/System/Library/Frameworks/System.framework" );
	url = CFURLCreateWithFileSystemPath( NULL, path, kCFURLPOSIXPathStyle, true );
	require_action_quiet( url, exit, err = memFullErr );
	
	bundle = CFBundleCreate( NULL, url );
	CFRelease( url );
	require_action_quiet( bundle, exit, err = memFullErr );
	
	// Get a ptr to the system's "printf" function from System.framework.
	
	functionName = CFSTR( "printf" );
	functionPtr = CFBundleGetFunctionPointerForName( bundle, functionName );
	require_action_quiet( functionPtr, exit, err = memFullErr );	
	
	// Success! Note: The bundle cannot be released because it would invalidate the function ptr.
	
	gDebugMacOSXLogFunction = (DebugMacOSXLogFunctionPtr) functionPtr;
	bundle = NULL;
	err = noErr;
	
exit:
	if( bundle )
	{
		CFRelease( bundle );
	}
	return( err );
}

//===========================================================================================================================
//	DebugMacOSXLogPrint
//===========================================================================================================================

static void	DebugMacOSXLogPrint( char *inData, size_t inSize )
{	
	if( gDebugMacOSXLogFunction )
	{
		gDebugMacOSXLogFunction( "%.*s", (int) inSize, inData );
	}
}
#endif

#if( TARGET_OS_WIN32 )
//===========================================================================================================================
//	DebugWindowsDebuggerPrint
//===========================================================================================================================

void	DebugWindowsDebuggerPrint( char *inData, size_t inSize )
{
	TCHAR				buffer[ 512 ];
	const char *		src;
	const char *		end;
	TCHAR *				dst;
	char				c;
	
	// Copy locally and null terminate the string. This also converts from char to TCHAR in case we are 
	// building with UNICODE enabled since the input is always char. Also convert \r to \n in the process.

	src = inData;
	if( inSize >= sizeof_array( buffer ) )
	{
		inSize = sizeof_array( buffer ) - 1;
	}
	end = src + inSize;
	dst = buffer;	
	while( src < end )
	{
		c = *src++;
		if( c == '\r' )
		{
			c = '\n';
		}
		*dst++ = (TCHAR) c;
	}
	*dst = 0;
	
	// Print out the string to the debugger.
	
	OutputDebugString( buffer );
}
#endif

#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
//===========================================================================================================================
//	DebugWindowsEventLogInit
//===========================================================================================================================

static OSStatus	DebugWindowsEventLogInit( const char *inName, HMODULE inModule )
{
	OSStatus			err;
	HKEY				key;
	TCHAR				name[ 128 ];
	const char *		src;
	TCHAR				path[ MAX_PATH ];
	size_t				size;
	DWORD				typesSupported;
	DWORD 				n;
	
	key = NULL;

	// Use a default name if needed then convert the name to TCHARs so it works on ANSI or Unicode builds.

	if( !inName || ( *inName == '\0' ) )
	{
		inName = "DefaultApp";
	}
	DebugWinCharToTCharString( inName, kSizeCString, name, sizeof( name ), NULL );
	
	// Build the path string using the fixed registry path and app name.
	
	src = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
	DebugWinCharToTCharString( src, kSizeCString, path, sizeof_array( path ), &size );
	DebugWinCharToTCharString( inName, kSizeCString, path + size, sizeof_array( path ) - size, NULL );
	
	// Add/Open the source name as a sub-key under the Application key in the EventLog registry key.
	
	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL );
	require_noerr_quiet( err, exit );
	
	// Set the path in the EventMessageFile subkey. Add 1 to the TCHAR count to include the null terminator.
	
	n = GetModuleFileName( inModule, path, sizeof_array( path ) );
	err = translate_errno( n > 0, (OSStatus) GetLastError(), kParamErr );
	require_noerr_quiet( err, exit );
	n += 1;
	n *= sizeof( TCHAR );
	
	err = RegSetValueEx( key, TEXT( "EventMessageFile" ), 0, REG_EXPAND_SZ, (const LPBYTE) path, n );
	require_noerr_quiet( err, exit );
	
	// Set the supported event types in the TypesSupported subkey.
	
	typesSupported = EVENTLOG_SUCCESS | EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE |
					 EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE;
	err = RegSetValueEx( key, TEXT( "TypesSupported" ), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) );
	require_noerr_quiet( err, exit );
	
	// Set up the event source.
	
	gDebugWindowsEventLogEventSource = RegisterEventSource( NULL, name );
	err = translate_errno( gDebugWindowsEventLogEventSource, (OSStatus) GetLastError(), kParamErr );
	require_noerr_quiet( err, exit );
	
exit:
	if( key )
	{
		RegCloseKey( key );
	}
	return( err );
}

//===========================================================================================================================
//	DebugWindowsEventLogPrint
//===========================================================================================================================

static void	DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize )
{
	WORD				type;
	TCHAR				buffer[ 512 ];
	const char *		src;
	const char *		end;
	TCHAR *				dst;
	char				c;
	const TCHAR *		array[ 1 ];
	
	// Map the debug level to a Windows EventLog type.
	
	if( inLevel <= kDebugLevelNotice )
	{
		type = EVENTLOG_INFORMATION_TYPE;
	}
	else if( inLevel <= kDebugLevelWarning )
	{
		type = EVENTLOG_WARNING_TYPE;
	}
	else
	{
		type = EVENTLOG_ERROR_TYPE;
	}
	
	// Copy locally and null terminate the string. This also converts from char to TCHAR in case we are 
	// building with UNICODE enabled since the input is always char. Also convert \r to \n in the process.
	
	src = inData;
	if( inSize >= sizeof_array( buffer ) )
	{
		inSize = sizeof_array( buffer ) - 1;
	}
	end = src + inSize;
	dst = buffer;	
	while( src < end )
	{
		c = *src++;
		if( c == '\r' )
		{
			c = '\n';
		}
		*dst++ = (TCHAR) c;
	}
	*dst = 0;
	
	// Add the the string to the event log.
	
	array[ 0 ] = buffer;
	if( gDebugWindowsEventLogEventSource )
	{
		ReportEvent( gDebugWindowsEventLogEventSource, type, 0, 0x20000001L, NULL, 1, 0, array, NULL );
	}
}
#endif	// TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE

#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
//===========================================================================================================================
//	DebugAssertOutputHandler
//===========================================================================================================================

static pascal void	
	DebugAssertOutputHandler( 
		OSType 				inComponentSignature, 
		UInt32 				inOptions, 
		const char *		inAssertString, 
		const char *		inExceptionString, 
		const char *		inErrorString, 
		const char *		inFileName, 
		long 				inLineNumber, 
		void *				inValue, 
		ConstStr255Param 	inOutputMsg )
{
	DEBUG_UNUSED( inComponentSignature );
	DEBUG_UNUSED( inOptions );
	DEBUG_UNUSED( inExceptionString );
	DEBUG_UNUSED( inValue );
	DEBUG_UNUSED( inOutputMsg );
	
	DebugPrintAssert( 0, inAssertString, inErrorString, inFileName, (int_least32_t) inLineNumber, "" );
}
#endif

#if 0
#pragma mark -
#pragma mark == Utilities ==
#endif

//===========================================================================================================================
//	DebugSNPrintF
//
//	Stolen from mDNS.c's mDNS_snprintf/mDNS_vsnprintf with the following changes:
//
//	Changed names to avoid name collisions with the mDNS versions.
//	Changed types to standard C types since mDNSEmbeddedAPI.h may not be available.
//	Conditionalized mDNS stuff so it can be used with or with mDNSEmbeddedAPI.h.
//	Added 64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
//	Added %@   - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
//	Added %.8a - FIbre Channel address. Arg=ptr to address.
//	Added %##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
//	Added %b   - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
//	Added %C   - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
//	Added %H   - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
//	Added %#H  - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
//	Added %m   - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code args are the same as %d, %x, etc.
//	Added %S   - UTF-16 string. Host order if no BOM. Precision is UTF-16 char count. BOM counts in any precision. Arg=ptr.
//	Added %#S  - Big Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S.
//	Added %##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S.
//	Added %U   - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
//===========================================================================================================================

DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...)
	{
	size_t length;
	
	va_list ptr;
	va_start(ptr,fmt);
	length = DebugSNPrintFVAList(sbuffer, buflen, fmt, ptr);
	va_end(ptr);
	
	return(length);
	}

//===========================================================================================================================
//	DebugSNPrintFVAList	- va_list version of DebugSNPrintF. See DebugSNPrintF for more info.
//===========================================================================================================================

DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg)
	{
	static const struct DebugSNPrintF_format
		{
		unsigned      leftJustify : 1;
		unsigned      forceSign : 1;
		unsigned      zeroPad : 1;
		unsigned      havePrecision : 1;
		unsigned      hSize : 1;
		char          lSize;
		char          altForm;
		char          sign;		// +, - or space
		unsigned int  fieldWidth;
		unsigned int  precision;
		} DebugSNPrintF_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

	size_t nwritten = 0;
	int c;
	if (buflen == 0) return(0);
	buflen--;		// Pre-reserve one space in the buffer for the terminating nul
	if (buflen == 0) goto exit;
	
	for (c = *fmt; c != 0; c = *++fmt)
		{
		if (c != '%')
			{
			*sbuffer++ = (char)c;
			if (++nwritten >= buflen) goto exit;
			}
		else
			{
			size_t i=0, j;
			// The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
			// generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
			// The size needs to be enough for a 256-byte domain name plus some error text.
			#define mDNS_VACB_Size 300
			char mDNS_VACB[mDNS_VACB_Size];
			#define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
			#define mDNS_VACB_Remain(s) ((size_t)(mDNS_VACB_Lim - s))
			char *s = mDNS_VACB_Lim;
			const char *digits = "0123456789ABCDEF";
			struct DebugSNPrintF_format F = DebugSNPrintF_format_default;
	
			for(;;)	//  decode flags
				{
				c = *++fmt;
				if      (c == '-') F.leftJustify = 1;
				else if (c == '+') F.forceSign = 1;
				else if (c == ' ') F.sign = ' ';
				else if (c == '#') F.altForm++;
				else if (c == '0') F.zeroPad = 1;
				else break;
				}
	
			if (c == '*')	//  decode field width
				{
				int f = va_arg(arg, int);
				if (f < 0) { f = -f; F.leftJustify = 1; }
				F.fieldWidth = (unsigned int)f;
				c = *++fmt;
				}
			else
				{
				for (; c >= '0' && c <= '9'; c = *++fmt)
					F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
				}
	
			if (c == '.')	//  decode precision
				{
				if ((c = *++fmt) == '*')
					{ F.precision = va_arg(arg, unsigned int); c = *++fmt; }
				else for (; c >= '0' && c <= '9'; c = *++fmt)
						F.precision = (10 * F.precision) + (c - '0');
				F.havePrecision = 1;
				}
	
			if (F.leftJustify) F.zeroPad = 0;
	
			conv:
			switch (c)	//  perform appropriate conversion
				{
				#if TYPE_LONGLONG_NATIVE
					unsigned_long_long_compat n;
					unsigned_long_long_compat base;
				#else
					unsigned long n;
					unsigned long base;
				#endif
				case 'h' :	F.hSize = 1; c = *++fmt; goto conv;
				case 'l' :	// fall through
				case 'L' :	F.lSize++; c = *++fmt; goto conv;
				case 'd' :
				case 'i' :	base = 10;
							goto canBeSigned;
				case 'u' :	base = 10;
							goto notSigned;
				case 'o' :	base = 8;
							goto notSigned;
				case 'b' :	base = 2;
							goto notSigned;
				case 'p' :	n = va_arg(arg, uintptr_t);
							F.havePrecision = 1;
							F.precision = (sizeof(uintptr_t) == 4) ? 8 : 16;
							F.sign = 0;
							base = 16;
							c = 'x';
							goto number;
				case 'x' :	digits = "0123456789abcdef";
				case 'X' :	base = 16;
							goto notSigned;
				canBeSigned:
							#if TYPE_LONGLONG_NATIVE
								if (F.lSize == 1) n = (unsigned_long_long_compat)va_arg(arg, long);
								else if (F.lSize == 2) n = (unsigned_long_long_compat)va_arg(arg, long_long_compat);
								else n = (unsigned_long_long_compat)va_arg(arg, int);
							#else
								if (F.lSize == 1) n = (unsigned long)va_arg(arg, long);
								else if (F.lSize == 2) goto exit;
								else n = (unsigned long)va_arg(arg, int);
							#endif
							if (F.hSize) n = (short) n;
							#if TYPE_LONGLONG_NATIVE
								if ((long_long_compat) n < 0) { n = (unsigned_long_long_compat)-(long_long_compat)n; F.sign = '-'; }
							#else
								if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
							#endif
							else if (F.forceSign) F.sign = '+';
							goto number;
				
				notSigned:	if (F.lSize == 1) n = va_arg(arg, unsigned long);
							else if (F.lSize == 2)
								{
								#if TYPE_LONGLONG_NATIVE
									n = va_arg(arg, unsigned_long_long_compat);
								#else
									goto exit;
								#endif
								}
							else n = va_arg(arg, unsigned int);
							if (F.hSize) n = (unsigned short) n;
							F.sign = 0;
							goto number;
				
				number:		if (!F.havePrecision)
								{
								if (F.zeroPad)
									{
									F.precision = F.fieldWidth;
									if (F.altForm) F.precision -= 2;
									if (F.sign) --F.precision;
									}
								if (F.precision < 1) F.precision = 1;
								}
							if (F.precision > mDNS_VACB_Size - 1)
								F.precision = mDNS_VACB_Size - 1;
							for (i = 0; n; n /= base, i++) *--s = (char)(digits[n % base]);
							for (; i < F.precision; i++) *--s = '0';
							if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
							if (F.sign) { *--s = F.sign; i++; }
							break;
	
				case 'a' :	{
							unsigned char *a = va_arg(arg, unsigned char *);
							char pre[4] = "";
							char post[32] = "";
							if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
							else
								{
								s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
								if (F.altForm == 1)
									{
									#if(defined(MDNS_DEBUGMSGS))
										mDNSAddr *ip = (mDNSAddr*)a;
										switch (ip->type)
											{
											case mDNSAddrType_IPv4: F.precision =  4; a = (unsigned char *)&ip->ip.v4; break;
											case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
											default:                F.precision =  0; break;
											}
									#else
										F.precision = 0;	// mDNSEmbeddedAPI.h not included so no mDNSAddr support
									#endif
									}
								else if (F.altForm == 2)
									{
									#ifdef AF_INET
										const struct sockaddr *sa;
										unsigned char *port;
										sa = (const struct sockaddr*)a;
										switch (sa->sa_family)
											{
											case AF_INET:  F.precision =  4; a = (unsigned char*)&((const struct sockaddr_in *)a)->sin_addr;
											               port = (unsigned char*)&((const struct sockaddr_in *)sa)->sin_port;
											               DebugSNPrintF(post, sizeof(post), ":%d", (port[0] << 8) | port[1]); break;
											#ifdef AF_INET6
											case AF_INET6: F.precision = 16; a = (unsigned char*)&((const struct sockaddr_in6 *)a)->sin6_addr; 
											               pre[0] = '['; pre[1] = '\0';
											               port = (unsigned char*)&((const struct sockaddr_in6 *)sa)->sin6_port;
											               DebugSNPrintF(post, sizeof(post), "%%%d]:%d", 
											               		(int)((const struct sockaddr_in6 *)sa)->sin6_scope_id,
											               		(port[0] << 8) | port[1]); break;
											#endif
											default:       F.precision =  0; break;
											}
									#else
										F.precision = 0;	// socket interfaces not included so no sockaddr support
									#endif
									}
								switch (F.precision)
									{
									case  4: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d%s",
														a[0], a[1], a[2], a[3], post); break;
									case  6: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
														a[0], a[1], a[2], a[3], a[4], a[5]); break;
									case  8: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
														a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); break;
									case 16: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), 
														"%s%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%s",
														pre, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], 
														a[9], a[10], a[11], a[12], a[13], a[14], a[15], post); break;		
									default: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size "
														"(i.e. %.4a=IPv4, %.6a=Ethernet, %.8a=Fibre Channel %.16a=IPv6) >>"); break;
									}
								}
							}
							break;

				case 'U' :	{
							unsigned char *a = va_arg(arg, unsigned char *);
							if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
							else
								{
								s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
								i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
										*((uint32_t*) &a[0]), *((uint16_t*) &a[4]), *((uint16_t*) &a[6]), 
										a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); break;
								}
							}
							break;

				case 'c' :	*--s = (char)va_arg(arg, int); i = 1; break;
	
				case 'C' :	if (F.lSize) n = va_arg(arg, unsigned long);
							else n = va_arg(arg, unsigned int);
							if (F.hSize) n = (unsigned short) n;
							c = (int)( n        & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
							c = (int)((n >>  8) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
							c = (int)((n >> 16) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
							c = (int)((n >> 24) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
							i = 4;
							break;
	
				case 's' :	s = va_arg(arg, char *);
							if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
							else switch (F.altForm)
								{
								case 0:	i=0;
										if (F.havePrecision)				// C string
											{
											while((i < F.precision) && s[i]) i++;
											// Make sure we don't truncate in the middle of a UTF-8 character.
											// If the last character is part of a multi-byte UTF-8 character, back up to the start of it.
											j=0;
											while((i > 0) && ((c = s[i-1]) & 0x80)) { j++; i--; if((c & 0xC0) != 0x80) break; }
											// If the actual count of UTF-8 characters matches the encoded UTF-8 count, add it back.
											if((j > 1) && (j <= 6))
												{
												int test = (0xFF << (8-j)) & 0xFF;
												int mask = test | (1 << ((8-j)-1));
												if((c & mask) == test) i += j;
												}
											}
										else
											while(s[i]) i++;
										break;								
								case 1: i = (unsigned char) *s++; break;	// Pascal string
								case 2: {									// DNS label-sequence name
										unsigned char *a = (unsigned char *)s;
										s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
										if (*a == 0) *s++ = '.';	// Special case for root DNS name
										while (*a)
											{
											if (*a > 63) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
											if (s + *a >= &mDNS_VACB[254]) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
											s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "%#s.", a);
											a += 1 + *a;
											}
										i = (size_t)(s - mDNS_VACB);
										s = mDNS_VACB;	// Reset s back to the start of the buffer
										break;
										}
								}
							if (F.havePrecision && i > F.precision)		// Make sure we don't truncate in the middle of a UTF-8 character
								{ i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
							break;
				
				case 'S':	{	// UTF-16 string
							unsigned char *a = va_arg(arg, unsigned char *);
							uint16_t      *u = (uint16_t*)a;
							if (!u) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
							if ((!F.havePrecision || F.precision))
								{
								if      ((a[0] == 0xFE) && (a[1] == 0xFF)) { F.altForm = 1; u += 1; a += 2; F.precision--; }	// Big Endian
								else if ((a[0] == 0xFF) && (a[1] == 0xFE)) { F.altForm = 2; u += 1; a += 2; F.precision--; }	// Little Endian
								}
							s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
							switch (F.altForm)
								{
								case 0:	while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s))	// Host Endian
											{ c = u[i]; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; }
										break;
								case 1: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s))	// Big Endian
											{ c = ((a[0] << 8) | a[1]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; }
										break;
								case 2: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s))	// Little Endian
											{ c = ((a[1] << 8) | a[0]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; }
										break;
								}
							}
							s = mDNS_VACB;	// Reset s back to the start of the buffer
							break;
			
			#if TARGET_OS_MAC
				case '@':	{	// Cocoa/CoreFoundation object
							CFTypeRef cfObj;
							CFStringRef cfStr;
							cfObj = (CFTypeRef) va_arg(arg, void *);
							cfStr = (CFGetTypeID(cfObj) == CFStringGetTypeID()) ? (CFStringRef)CFRetain(cfObj) : CFCopyDescription(cfObj);
							s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
							if (cfStr)
								{
								CFRange range;
								CFIndex m;
								range = CFRangeMake(0, CFStringGetLength(cfStr));
								m = 0;
								CFStringGetBytes(cfStr, range, kCFStringEncodingUTF8, '^', false, (UInt8*)mDNS_VACB, (CFIndex)sizeof(mDNS_VACB), &m);
								CFRelease(cfStr);
								i = (size_t) m;
								}
							else
								{
								i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "ERROR: <invalid CF object>" );
								}
							}
							if (F.havePrecision && i > F.precision)		// Make sure we don't truncate in the middle of a UTF-8 character
								{ i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
							break;
			#endif

				case 'm' :	{	// Error Message
							long err;
							if (F.lSize) err = va_arg(arg, long);
							else err = va_arg(arg, int);
							if (F.hSize) err = (short)err;
							DebugGetErrorString(err, mDNS_VACB, sizeof(mDNS_VACB));
							s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
							for(i=0;s[i];i++) {}
							}
							break;

				case 'H' :	{	// Hex Dump
							void *a = va_arg(arg, void *);
							size_t size = (size_t)va_arg(arg, int);
							size_t max = (size_t)va_arg(arg, int);
							DebugFlags flags = 
								kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine |
								kDebugFlags8BitSeparator | kDebugFlagsNo32BitSeparator |
								kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount;
							if (F.altForm == 0) flags |= kDebugFlagsNoASCII;
							size = (max < size) ? max : size;
							s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
							i = DebugHexDump(kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, a, a, size, flags, mDNS_VACB, sizeof(mDNS_VACB));
							}
							break;
				
				case 'v' :	{	// Version
							uint32_t version;
							version = va_arg(arg, unsigned int);
							DebugNumVersionToString(version, mDNS_VACB);
							s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
							for(i=0;s[i];i++) {}
							}
							break;

				case 'n' :	s = va_arg(arg, char *);
							if      (F.hSize) * (short *) s = (short)nwritten;
							else if (F.lSize) * (long  *) s = (long)nwritten;
							else              * (int   *) s = (int)nwritten;
							continue;
	
				default:	s = mDNS_VACB;
							i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);

				case '%' :	*sbuffer++ = (char)c;
							if (++nwritten >= buflen) goto exit;
							break;
				}
	
			if (i < F.fieldWidth && !F.leftJustify)			// Pad on the left
				do	{
					*sbuffer++ = ' ';
					if (++nwritten >= buflen) goto exit;
					} while (i < --F.fieldWidth);
	
			if (i > buflen - nwritten)	// Make sure we don't truncate in the middle of a UTF-8 character
				{ i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
			for (j=0; j<i; j++) *sbuffer++ = *s++;			// Write the converted result
			nwritten += i;
			if (nwritten >= buflen) goto exit;
	
			for (; i < F.fieldWidth; i++)					// Pad on the right
				{
				*sbuffer++ = ' ';
				if (++nwritten >= buflen) goto exit;
				}
			}
		}
	exit:
	*sbuffer++ = 0;
	return(nwritten);
	}

//===========================================================================================================================
//	DebugGetErrorString
//===========================================================================================================================

DEBUG_EXPORT const char *	DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize )
{
	const char *		s;
	char *				dst;
	char *				end;
#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
	char				buffer[ 256 ];
#endif
	
	switch( inErrorCode )
	{
		#define	CaseErrorString( X, STR )					case X: s = STR; break
		#define	CaseErrorStringify( X )						case X: s = # X; break
		#define	CaseErrorStringifyHardCode( VALUE, X )		case VALUE: s = # X; break
		
		// General Errors
		
		CaseErrorString( 0,  "no error" );
		CaseErrorString( 1,  "in-progress/waiting" );
		CaseErrorString( -1, "catch-all unknown error" );
				
		// ACP Errors
		
		CaseErrorStringifyHardCode( -2,  kACPBadRequestErr );
		CaseErrorStringifyHardCode( -3,  kACPNoMemoryErr );
		CaseErrorStringifyHardCode( -4,  kACPBadParamErr );
		CaseErrorStringifyHardCode( -5,  kACPNotFoundErr );
		CaseErrorStringifyHardCode( -6,  kACPBadChecksumErr );
		CaseErrorStringifyHardCode( -7,  kACPCommandNotHandledErr );
		CaseErrorStringifyHardCode( -8,  kACPNetworkErr );
		CaseErrorStringifyHardCode( -9,  kACPDuplicateCommandHandlerErr );
		CaseErrorStringifyHardCode( -10, kACPUnknownPropertyErr );
		CaseErrorStringifyHardCode( -11, kACPImmutablePropertyErr );
		CaseErrorStringifyHardCode( -12, kACPBadPropertyValueErr );
		CaseErrorStringifyHardCode( -13, kACPNoResourcesErr );
		CaseErrorStringifyHardCode( -14, kACPBadOptionErr );
		CaseErrorStringifyHardCode( -15, kACPBadSizeErr );
		CaseErrorStringifyHardCode( -16, kACPBadPasswordErr );
		CaseErrorStringifyHardCode( -17, kACPNotInitializedErr );
		CaseErrorStringifyHardCode( -18, kACPNonReadablePropertyErr );
		CaseErrorStringifyHardCode( -19, kACPBadVersionErr );
		CaseErrorStringifyHardCode( -20, kACPBadSignatureErr );
		CaseErrorStringifyHardCode( -21, kACPBadIndexErr );
		CaseErrorStringifyHardCode( -22, kACPUnsupportedErr );
		CaseErrorStringifyHardCode( -23, kACPInUseErr );
		CaseErrorStringifyHardCode( -24, kACPParamCountErr );
		CaseErrorStringifyHardCode( -25, kACPIDErr );
		CaseErrorStringifyHardCode( -26, kACPFormatErr );
		CaseErrorStringifyHardCode( -27, kACPUnknownUserErr );
		CaseErrorStringifyHardCode( -28, kACPAccessDeniedErr );
		CaseErrorStringifyHardCode( -29, kACPIncorrectFWErr );
		
		// Common Services Errors
		
		CaseErrorStringify( kUnknownErr );
		CaseErrorStringify( kOptionErr );
		CaseErrorStringify( kSelectorErr );
		CaseErrorStringify( kExecutionStateErr );
		CaseErrorStringify( kPathErr );
		CaseErrorStringify( kParamErr );
		CaseErrorStringify( kParamCountErr );
		CaseErrorStringify( kCommandErr );
		CaseErrorStringify( kIDErr );
		CaseErrorStringify( kStateErr );
		CaseErrorStringify( kRangeErr );
		CaseErrorStringify( kRequestErr );
		CaseErrorStringify( kResponseErr );
		CaseErrorStringify( kChecksumErr );
		CaseErrorStringify( kNotHandledErr );
		CaseErrorStringify( kVersionErr );
		CaseErrorStringify( kSignatureErr );
		CaseErrorStringify( kFormatErr );
		CaseErrorStringify( kNotInitializedErr );
		CaseErrorStringify( kAlreadyInitializedErr );
		CaseErrorStringify( kNotInUseErr );
		CaseErrorStringify( kInUseErr );
		CaseErrorStringify( kTimeoutErr );
		CaseErrorStringify( kCanceledErr );
		CaseErrorStringify( kAlreadyCanceledErr );
		CaseErrorStringify( kCannotCancelErr );
		CaseErrorStringify( kDeletedErr );
		CaseErrorStringify( kNotFoundErr );
		CaseErrorStringify( kNoMemoryErr );
		CaseErrorStringify( kNoResourcesErr );
		CaseErrorStringify( kDuplicateErr );
		CaseErrorStringify( kImmutableErr );
		CaseErrorStringify( kUnsupportedDataErr );
		CaseErrorStringify( kIntegrityErr );
		CaseErrorStringify( kIncompatibleErr );
		CaseErrorStringify( kUnsupportedErr );
		CaseErrorStringify( kUnexpectedErr );
		CaseErrorStringify( kValueErr );
		CaseErrorStringify( kNotReadableErr );
		CaseErrorStringify( kNotWritableErr );
		CaseErrorStringify( kBadReferenceErr );
		CaseErrorStringify( kFlagErr );
		CaseErrorStringify( kMalformedErr );
		CaseErrorStringify( kSizeErr );
		CaseErrorStringify( kNameErr );
		CaseErrorStringify( kNotReadyErr );
		CaseErrorStringify( kReadErr );
		CaseErrorStringify( kWriteErr );
		CaseErrorStringify( kMismatchErr );
		CaseErrorStringify( kDateErr );
		CaseErrorStringify( kUnderrunErr );
		CaseErrorStringify( kOverrunErr );
		CaseErrorStringify( kEndingErr );
		CaseErrorStringify( kConnectionErr );
		CaseErrorStringify( kAuthenticationErr );
		CaseErrorStringify( kOpenErr );
		CaseErrorStringify( kTypeErr );
		CaseErrorStringify( kSkipErr );
		CaseErrorStringify( kNoAckErr );
		CaseErrorStringify( kCollisionErr );
		CaseErrorStringify( kBackoffErr );
		CaseErrorStringify( kNoAddressAckErr );
		CaseErrorStringify( kBusyErr );
		CaseErrorStringify( kNoSpaceErr );
		
		// mDNS/DNS-SD Errors
		
		CaseErrorStringifyHardCode( -65537, mStatus_UnknownErr );
		CaseErrorStringifyHardCode( -65538, mStatus_NoSuchNameErr );
		CaseErrorStringifyHardCode( -65539, mStatus_NoMemoryErr );
		CaseErrorStringifyHardCode( -65540, mStatus_BadParamErr );
		CaseErrorStringifyHardCode( -65541, mStatus_BadReferenceErr );
		CaseErrorStringifyHardCode( -65542, mStatus_BadStateErr );
		CaseErrorStringifyHardCode( -65543, mStatus_BadFlagsErr );
		CaseErrorStringifyHardCode( -65544, mStatus_UnsupportedErr );
		CaseErrorStringifyHardCode( -65545, mStatus_NotInitializedErr );
		CaseErrorStringifyHardCode( -65546, mStatus_NoCache );
		CaseErrorStringifyHardCode( -65547, mStatus_AlreadyRegistered );
		CaseErrorStringifyHardCode( -65548, mStatus_NameConflict );
		CaseErrorStringifyHardCode( -65549, mStatus_Invalid );
		CaseErrorStringifyHardCode( -65550, mStatus_GrowCache );
		CaseErrorStringifyHardCode( -65551, mStatus_BadInterfaceErr );
		CaseErrorStringifyHardCode( -65552, mStatus_Incompatible );
		CaseErrorStringifyHardCode( -65791, mStatus_ConfigChanged );
		CaseErrorStringifyHardCode( -65792, mStatus_MemFree );
		
		// RSP Errors
		
		CaseErrorStringifyHardCode( -400000, kRSPUnknownErr );
		CaseErrorStringifyHardCode( -400050, kRSPParamErr );
		CaseErrorStringifyHardCode( -400108, kRSPNoMemoryErr );
		CaseErrorStringifyHardCode( -405246, kRSPRangeErr );
		CaseErrorStringifyHardCode( -409057, kRSPSizeErr );
		CaseErrorStringifyHardCode( -400200, kRSPHardwareErr );
		CaseErrorStringifyHardCode( -401712, kRSPTimeoutErr );	
		CaseErrorStringifyHardCode( -402053, kRSPUnsupportedErr );
		CaseErrorStringifyHardCode( -402419, kRSPIDErr );
		CaseErrorStringifyHardCode( -403165, kRSPFlagErr );
		CaseErrorString( 			-200000, "kRSPControllerStatusBase - 0x50" );		
		CaseErrorString(			-200080, "kRSPCommandSucceededErr - 0x50" );
		CaseErrorString( 			-200001, "kRSPCommandFailedErr - 0x01" );
		CaseErrorString( 			-200051, "kRSPChecksumErr - 0x33" );
		CaseErrorString( 			-200132, "kRSPCommandTimeoutErr - 0x84" );
		CaseErrorString( 			-200034, "kRSPPasswordRequiredErr - 0x22 OBSOLETE" );
		CaseErrorString( 			-200128, "kRSPCanceledErr - 0x02 Async" );
		
		// XML Errors
		
		CaseErrorStringifyHardCode( -100043, kXMLNotFoundErr );
		CaseErrorStringifyHardCode( -100050, kXMLParamErr );
		CaseErrorStringifyHardCode( -100108, kXMLNoMemoryErr );
		CaseErrorStringifyHardCode( -100206, kXMLFormatErr );
		CaseErrorStringifyHardCode( -100586, kXMLNoRootElementErr );
		CaseErrorStringifyHardCode( -101703, kXMLWrongDataTypeErr );
		CaseErrorStringifyHardCode( -101726, kXMLKeyErr );
		CaseErrorStringifyHardCode( -102053, kXMLUnsupportedErr );
		CaseErrorStringifyHardCode( -102063, kXMLMissingElementErr );
		CaseErrorStringifyHardCode( -103026, kXMLParseErr );
		CaseErrorStringifyHardCode( -103159, kXMLBadDataErr );
		CaseErrorStringifyHardCode( -103170, kXMLBadNameErr );
		CaseErrorStringifyHardCode( -105246, kXMLRangeErr );
		CaseErrorStringifyHardCode( -105251, kXMLUnknownElementErr );
		CaseErrorStringifyHardCode( -108739, kXMLMalformedInputErr );
		CaseErrorStringifyHardCode( -109057, kXMLBadSizeErr );
		CaseErrorStringifyHardCode( -101730, kXMLMissingChildElementErr );
		CaseErrorStringifyHardCode( -102107, kXMLMissingParentElementErr );
		CaseErrorStringifyHardCode( -130587, kXMLNonRootElementErr );
		CaseErrorStringifyHardCode( -102015, kXMLDateErr );

	#if( __MACH__ )
	
		// Mach Errors

		CaseErrorStringifyHardCode( 0x00002000, MACH_MSG_IPC_SPACE );
		CaseErrorStringifyHardCode( 0x00001000, MACH_MSG_VM_SPACE );
		CaseErrorStringifyHardCode( 0x00000800, MACH_MSG_IPC_KERNEL );
		CaseErrorStringifyHardCode( 0x00000400, MACH_MSG_VM_KERNEL );
		CaseErrorStringifyHardCode( 0x10000001, MACH_SEND_IN_PROGRESS );
		CaseErrorStringifyHardCode( 0x10000002, MACH_SEND_INVALID_DATA );
		CaseErrorStringifyHardCode( 0x10000003, MACH_SEND_INVALID_DEST );
		CaseErrorStringifyHardCode( 0x10000004, MACH_SEND_TIMED_OUT );
		CaseErrorStringifyHardCode( 0x10000007, MACH_SEND_INTERRUPTED );
		CaseErrorStringifyHardCode( 0x10000008, MACH_SEND_MSG_TOO_SMALL );
		CaseErrorStringifyHardCode( 0x10000009, MACH_SEND_INVALID_REPLY );
		CaseErrorStringifyHardCode( 0x1000000A, MACH_SEND_INVALID_RIGHT );
		CaseErrorStringifyHardCode( 0x1000000B, MACH_SEND_INVALID_NOTIFY );
		CaseErrorStringifyHardCode( 0x1000000C, MACH_SEND_INVALID_MEMORY );
		CaseErrorStringifyHardCode( 0x1000000D, MACH_SEND_NO_BUFFER );
		CaseErrorStringifyHardCode( 0x1000000E, MACH_SEND_TOO_LARGE );
		CaseErrorStringifyHardCode( 0x1000000F, MACH_SEND_INVALID_TYPE );
		CaseErrorStringifyHardCode( 0x10000010, MACH_SEND_INVALID_HEADER );
		CaseErrorStringifyHardCode( 0x10000011, MACH_SEND_INVALID_TRAILER );
		CaseErrorStringifyHardCode( 0x10000015, MACH_SEND_INVALID_RT_OOL_SIZE );
		CaseErrorStringifyHardCode( 0x10004001, MACH_RCV_IN_PROGRESS );
		CaseErrorStringifyHardCode( 0x10004002, MACH_RCV_INVALID_NAME );
		CaseErrorStringifyHardCode( 0x10004003, MACH_RCV_TIMED_OUT );
		CaseErrorStringifyHardCode( 0x10004004, MACH_RCV_TOO_LARGE );
		CaseErrorStringifyHardCode( 0x10004005, MACH_RCV_INTERRUPTED );
		CaseErrorStringifyHardCode( 0x10004006, MACH_RCV_PORT_CHANGED );
		CaseErrorStringifyHardCode( 0x10004007, MACH_RCV_INVALID_NOTIFY );
		CaseErrorStringifyHardCode( 0x10004008, MACH_RCV_INVALID_DATA );
		CaseErrorStringifyHardCode( 0x10004009, MACH_RCV_PORT_DIED );
		CaseErrorStringifyHardCode( 0x1000400A, MACH_RCV_IN_SET );
		CaseErrorStringifyHardCode( 0x1000400B, MACH_RCV_HEADER_ERROR );
		CaseErrorStringifyHardCode( 0x1000400C, MACH_RCV_BODY_ERROR );
		CaseErrorStringifyHardCode( 0x1000400D, MACH_RCV_INVALID_TYPE );
		CaseErrorStringifyHardCode( 0x1000400E, MACH_RCV_SCATTER_SMALL );
		CaseErrorStringifyHardCode( 0x1000400F, MACH_RCV_INVALID_TRAILER );
		CaseErrorStringifyHardCode( 0x10004011, MACH_RCV_IN_PROGRESS_TIMED );

		// Mach OSReturn Errors

		CaseErrorStringifyHardCode( 0xDC000001, kOSReturnError );
		CaseErrorStringifyHardCode( 0xDC004001, kOSMetaClassInternal );
		CaseErrorStringifyHardCode( 0xDC004002, kOSMetaClassHasInstances );
		CaseErrorStringifyHardCode( 0xDC004003, kOSMetaClassNoInit );
		CaseErrorStringifyHardCode( 0xDC004004, kOSMetaClassNoTempData );
		CaseErrorStringifyHardCode( 0xDC004005, kOSMetaClassNoDicts );
		CaseErrorStringifyHardCode( 0xDC004006, kOSMetaClassNoKModSet );
		CaseErrorStringifyHardCode( 0xDC004007, kOSMetaClassNoInsKModSet );
		CaseErrorStringifyHardCode( 0xDC004008, kOSMetaClassNoSuper );
		CaseErrorStringifyHardCode( 0xDC004009, kOSMetaClassInstNoSuper );
		CaseErrorStringifyHardCode( 0xDC00400A, kOSMetaClassDuplicateClass );

		// IOKit Errors

		CaseErrorStringifyHardCode( 0xE00002BC, kIOReturnError );
		CaseErrorStringifyHardCode( 0xE00002BD, kIOReturnNoMemory );
		CaseErrorStringifyHardCode( 0xE00002BE, kIOReturnNoResources );
		CaseErrorStringifyHardCode( 0xE00002BF, kIOReturnIPCError );
		CaseErrorStringifyHardCode( 0xE00002C0, kIOReturnNoDevice );
		CaseErrorStringifyHardCode( 0xE00002C1, kIOReturnNotPrivileged );
		CaseErrorStringifyHardCode( 0xE00002C2, kIOReturnBadArgument );
		CaseErrorStringifyHardCode( 0xE00002C3, kIOReturnLockedRead );
		CaseErrorStringifyHardCode( 0xE00002C4, kIOReturnLockedWrite );
		CaseErrorStringifyHardCode( 0xE00002C5, kIOReturnExclusiveAccess );
		CaseErrorStringifyHardCode( 0xE00002C6, kIOReturnBadMessageID );
		CaseErrorStringifyHardCode( 0xE00002C7, kIOReturnUnsupported );
		CaseErrorStringifyHardCode( 0xE00002C8, kIOReturnVMError );
		CaseErrorStringifyHardCode( 0xE00002C9, kIOReturnInternalError );
		CaseErrorStringifyHardCode( 0xE00002CA, kIOReturnIOError );
		CaseErrorStringifyHardCode( 0xE00002CC, kIOReturnCannotLock );
		CaseErrorStringifyHardCode( 0xE00002CD, kIOReturnNotOpen );
		CaseErrorStringifyHardCode( 0xE00002CE, kIOReturnNotReadable );
		CaseErrorStringifyHardCode( 0xE00002CF, kIOReturnNotWritable );
		CaseErrorStringifyHardCode( 0xE00002D0, kIOReturnNotAligned );
		CaseErrorStringifyHardCode( 0xE00002D1, kIOReturnBadMedia );
		CaseErrorStringifyHardCode( 0xE00002D2, kIOReturnStillOpen );
		CaseErrorStringifyHardCode( 0xE00002D3, kIOReturnRLDError );
		CaseErrorStringifyHardCode( 0xE00002D4, kIOReturnDMAError );
		CaseErrorStringifyHardCode( 0xE00002D5, kIOReturnBusy );
		CaseErrorStringifyHardCode( 0xE00002D6, kIOReturnTimeout );
		CaseErrorStringifyHardCode( 0xE00002D7, kIOReturnOffline );
		CaseErrorStringifyHardCode( 0xE00002D8, kIOReturnNotReady );
		CaseErrorStringifyHardCode( 0xE00002D9, kIOReturnNotAttached );
		CaseErrorStringifyHardCode( 0xE00002DA, kIOReturnNoChannels );
		CaseErrorStringifyHardCode( 0xE00002DB, kIOReturnNoSpace );
		CaseErrorStringifyHardCode( 0xE00002DD, kIOReturnPortExists );
		CaseErrorStringifyHardCode( 0xE00002DE, kIOReturnCannotWire );
		CaseErrorStringifyHardCode( 0xE00002DF, kIOReturnNoInterrupt );
		CaseErrorStringifyHardCode( 0xE00002E0, kIOReturnNoFrames );
		CaseErrorStringifyHardCode( 0xE00002E1, kIOReturnMessageTooLarge );
		CaseErrorStringifyHardCode( 0xE00002E2, kIOReturnNotPermitted );
		CaseErrorStringifyHardCode( 0xE00002E3, kIOReturnNoPower );
		CaseErrorStringifyHardCode( 0xE00002E4, kIOReturnNoMedia );
		CaseErrorStringifyHardCode( 0xE00002E5, kIOReturnUnformattedMedia );
		CaseErrorStringifyHardCode( 0xE00002E6, kIOReturnUnsupportedMode );
		CaseErrorStringifyHardCode( 0xE00002E7, kIOReturnUnderrun );
		CaseErrorStringifyHardCode( 0xE00002E8, kIOReturnOverrun );
		CaseErrorStringifyHardCode( 0xE00002E9, kIOReturnDeviceError	 );
		CaseErrorStringifyHardCode( 0xE00002EA, kIOReturnNoCompletion	 );
		CaseErrorStringifyHardCode( 0xE00002EB, kIOReturnAborted	 );
		CaseErrorStringifyHardCode( 0xE00002EC, kIOReturnNoBandwidth	 );
		CaseErrorStringifyHardCode( 0xE00002ED, kIOReturnNotResponding	 );
		CaseErrorStringifyHardCode( 0xE00002EE, kIOReturnIsoTooOld	 );
		CaseErrorStringifyHardCode( 0xE00002EF, kIOReturnIsoTooNew	 );
		CaseErrorStringifyHardCode( 0xE00002F0, kIOReturnNotFound );
		CaseErrorStringifyHardCode( 0xE0000001, kIOReturnInvalid );

		// IOKit FireWire Errors

		CaseErrorStringifyHardCode( 0xE0008010, kIOFireWireResponseBase );
		CaseErrorStringifyHardCode( 0xE0008020, kIOFireWireBusReset );
		CaseErrorStringifyHardCode( 0xE0008001, kIOConfigNoEntry );
		CaseErrorStringifyHardCode( 0xE0008002, kIOFireWirePending );
		CaseErrorStringifyHardCode( 0xE0008003, kIOFireWireLastDCLToken );
		CaseErrorStringifyHardCode( 0xE0008004, kIOFireWireConfigROMInvalid );
		CaseErrorStringifyHardCode( 0xE0008005, kIOFireWireAlreadyRegistered );
		CaseErrorStringifyHardCode( 0xE0008006, kIOFireWireMultipleTalkers );
		CaseErrorStringifyHardCode( 0xE0008007, kIOFireWireChannelActive );
		CaseErrorStringifyHardCode( 0xE0008008, kIOFireWireNoListenerOrTalker );
		CaseErrorStringifyHardCode( 0xE0008009, kIOFireWireNoChannels );
		CaseErrorStringifyHardCode( 0xE000800A, kIOFireWireChannelNotAvailable );
		CaseErrorStringifyHardCode( 0xE000800B, kIOFireWireSeparateBus );
		CaseErrorStringifyHardCode( 0xE000800C, kIOFireWireBadSelfIDs );
		CaseErrorStringifyHardCode( 0xE000800D, kIOFireWireLowCableVoltage );
		CaseErrorStringifyHardCode( 0xE000800E, kIOFireWireInsufficientPower );
		CaseErrorStringifyHardCode( 0xE000800F, kIOFireWireOutOfTLabels );
		CaseErrorStringifyHardCode( 0xE0008101, kIOFireWireBogusDCLProgram );
		CaseErrorStringifyHardCode( 0xE0008102, kIOFireWireTalkingAndListening );
		CaseErrorStringifyHardCode( 0xE0008103, kIOFireWireHardwareSlept );
		CaseErrorStringifyHardCode( 0xE00087D0, kIOFWMessageServiceIsRequestingClose );
		CaseErrorStringifyHardCode( 0xE00087D1, kIOFWMessagePowerStateChanged );
		CaseErrorStringifyHardCode( 0xE00087D2, kIOFWMessageTopologyChanged );

		// IOKit USB Errors
				
		CaseErrorStringifyHardCode( 0xE0004061, kIOUSBUnknownPipeErr );
		CaseErrorStringifyHardCode( 0xE0004060, kIOUSBTooManyPipesErr );
		CaseErrorStringifyHardCode( 0xE000405F, kIOUSBNoAsyncPortErr );
		CaseErrorStringifyHardCode( 0xE000405E, kIOUSBNotEnoughPipesErr );
		CaseErrorStringifyHardCode( 0xE000405D, kIOUSBNotEnoughPowerErr );
		CaseErrorStringifyHardCode( 0xE0004057, kIOUSBEndpointNotFound );
		CaseErrorStringifyHardCode( 0xE0004056, kIOUSBConfigNotFound );
		CaseErrorStringifyHardCode( 0xE0004051, kIOUSBTransactionTimeout );
		CaseErrorStringifyHardCode( 0xE0004050, kIOUSBTransactionReturned );
		CaseErrorStringifyHardCode( 0xE000404F, kIOUSBPipeStalled );
		CaseErrorStringifyHardCode( 0xE000404E, kIOUSBInterfaceNotFound );
		CaseErrorStringifyHardCode( 0xE000404D, kIOUSBLowLatencyBufferNotPreviouslyAllocated );
		CaseErrorStringifyHardCode( 0xE000404C, kIOUSBLowLatencyFrameListNotPreviouslyAllocated );
		CaseErrorStringifyHardCode( 0xE000404B, kIOUSBHighSpeedSplitError );
		CaseErrorStringifyHardCode( 0xE0004010, kIOUSBLinkErr );
		CaseErrorStringifyHardCode( 0xE000400F, kIOUSBNotSent2Err );
		CaseErrorStringifyHardCode( 0xE000400E, kIOUSBNotSent1Err );
		CaseErrorStringifyHardCode( 0xE000400D, kIOUSBBufferUnderrunErr );
		CaseErrorStringifyHardCode( 0xE000400C, kIOUSBBufferOverrunErr );
		CaseErrorStringifyHardCode( 0xE000400B, kIOUSBReserved2Err );
		CaseErrorStringifyHardCode( 0xE000400A, kIOUSBReserved1Err );
		CaseErrorStringifyHardCode( 0xE0004007, kIOUSBWrongPIDErr );
		CaseErrorStringifyHardCode( 0xE0004006, kIOUSBPIDCheckErr );
		CaseErrorStringifyHardCode( 0xE0004003, kIOUSBDataToggleErr );
		CaseErrorStringifyHardCode( 0xE0004002, kIOUSBBitstufErr );
		CaseErrorStringifyHardCode( 0xE0004001, kIOUSBCRCErr );
	
	#endif	// __MACH__

		// Other Errors
		
		default:
			s = NULL;
			#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
				if( inBuffer && ( inBufferSize > 0 ) )
				{
					DWORD		n;
					
					n = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) inErrorCode, 
						MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), buffer, sizeof( buffer ), NULL );
					if( n > 0 )
					{
						// Remove any trailing CR's or LF's since some messages have them.
						
						while( ( n > 0 ) && isspace( ( (unsigned char *) buffer )[ n - 1 ] ) )
						{
							buffer[ --n ] = '\0';
						}
						s = buffer;
					}
				}
			#endif
			
			if( !s )
			{
				#if( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE )
					s = strerror( inErrorCode );
				#endif
				if( !s )
				{
					s = "<unknown error code>";
				}
			}
			break;
	}
	
	// Copy the string to the output buffer. If no buffer is supplied or it is empty, return an empty string.
	
	if( inBuffer && ( inBufferSize > 0 ) )
	{
		dst = inBuffer;
		end = dst + ( inBufferSize - 1 );
		while( ( ( end - dst ) > 0 ) && ( *s != '\0' ) )
		{
			*dst++ = *s++;
		}
		*dst = '\0';
		s = inBuffer;
	}
	return( s );
}

//===========================================================================================================================
//	DebugHexDump
//===========================================================================================================================

DEBUG_EXPORT size_t
	DebugHexDump( 
		DebugLevel		inLevel, 
		int				inIndent, 
		const char * 	inLabel, 
		size_t 			inLabelSize, 
		int				inLabelMinWidth, 
		const char *	inType, 
		size_t 			inTypeSize, 
		const void *	inDataStart, 
		const void *	inData, 
		size_t 			inDataSize, 
		DebugFlags	 	inFlags, 
		char *			outBuffer, 
		size_t			inBufferSize )
{
	static const char		kHexChars[] = "0123456789ABCDEF";
	const uint8_t *			start;
	const uint8_t *			src;
	char *					dst;
	char *					end;
	size_t					n;
	int						offset;
	int						width;
	const char *			newline;
	char					separator[ 8 ];
	char *					s;
	
	DEBUG_UNUSED( inType );
	DEBUG_UNUSED( inTypeSize );
	
	// Set up the function-wide variables.
	
	if( inLabelSize == kSizeCString )
	{
		inLabelSize = strlen( inLabel );
	}
	start 	= (const uint8_t *) inData;
	src 	= start;
	dst		= outBuffer;
	end		= dst + inBufferSize;
	offset 	= (int)( (intptr_t) inData - (intptr_t) inDataStart );
	width	= ( (int) inLabelSize > inLabelMinWidth ) ? (int) inLabelSize : inLabelMinWidth;
	newline	= ( inFlags & kDebugFlagsNoNewLine ) ? "" : "\n";
		
	// Set up the separator string. This is used to insert spaces on subsequent "lines" when not using newlines.
	
	s = separator;
	if( inFlags & kDebugFlagsNoNewLine )
	{
		if( inFlags & kDebugFlags8BitSeparator )
		{
			*s++ = ' ';
		}
		if( inFlags & kDebugFlags16BitSeparator )
		{
			*s++ = ' ';
		}
		if( !( inFlags & kDebugFlagsNo32BitSeparator ) )
		{
			*s++ = ' ';
		}
		check( ( (size_t)( s - separator ) ) < sizeof( separator ) );
	}
	*s = '\0';
	
	for( ;; )
	{
		char		prefixString[ 32 ];
		char		hexString[ 64 ];
		char		asciiString[ 32 ];
		char		byteCountString[ 32 ];
		int			c;
		size_t		chunkSize;
		size_t		i;
		
		// If this is a label-only item (i.e. no data), print the label (accounting for prefix string spacing) and exit.
		
		if( inDataSize == 0 )
		{
			if( inLabel && ( inLabelSize > 0 ) )
			{
				width = 0;
				if( !( inFlags & kDebugFlagsNoAddress ) )
				{
					width += 8;			// "00000000"
					if( !( inFlags & kDebugFlagsNoOffset ) )
					{
						width += 1;		// "+"
					}
				}
				if( inFlags & kDebugFlags32BitOffset )
				{
					width += 8;			// "00000000"
				}
				else if( !( inFlags & kDebugFlagsNoOffset ) )
				{
					width += 4;			// "0000"
				}
				
				if( outBuffer )
				{
					dst += DebugSNPrintF( dst, (size_t)( end - dst ), "%*s" "%-*.*s" "%.*s" "%s", 
						width, "", 
						( width > 0 ) ? ": " : "", 
						width, (int) inLabelSize, inLabel, 
						newline );
				}
				else
				{
					dst += DebugPrintF( inLevel, "%*s" "%-*.*s" "%.*s" "%s", 
						width, "", 
						( width > 0 ) ? ": " : "", 
						width, (int) inLabelSize, inLabel, 
						newline );
				}
			}
			break;
		}
		
		// Build the prefix string. It will be in one of the following formats:
		//
		// 1) "00000000+0000[0000]"	(address and offset)
		// 2) "00000000"			(address only)
		// 3) "0000[0000]"			(offset only)
		// 4) ""					(no address or offset)
		//
		// Note: If we're printing multiple "lines", but not printing newlines, a space is used to separate.
		
		s = prefixString;
		if( !( inFlags & kDebugFlagsNoAddress ) )
		{
			*s++ = kHexChars[ ( ( (uintptr_t) src ) >> 28 ) & 0xF ];
			*s++ = kHexChars[ ( ( (uintptr_t) src ) >> 24 ) & 0xF ];
			*s++ = kHexChars[ ( ( (uintptr_t) src ) >> 20 ) & 0xF ];
			*s++ = kHexChars[ ( ( (uintptr_t) src ) >> 16 ) & 0xF ];
			*s++ = kHexChars[ ( ( (uintptr_t) src ) >> 12 ) & 0xF ];
			*s++ = kHexChars[ ( ( (uintptr_t) src ) >>  8 ) & 0xF ];
			*s++ = kHexChars[ ( ( (uintptr_t) src ) >>  4 ) & 0xF ];
			*s++ = kHexChars[   ( (uintptr_t) src )         & 0xF ];
			
			if( !( inFlags & kDebugFlagsNoOffset ) )
			{
				*s++ = '+';
			}
		}
		if( !( inFlags & kDebugFlagsNoOffset ) )
		{
			if( inFlags & kDebugFlags32BitOffset )
			{
				*s++ = kHexChars[ ( offset >> 28 ) & 0xF ];
				*s++ = kHexChars[ ( offset >> 24 ) & 0xF ];
				*s++ = kHexChars[ ( offset >> 20 ) & 0xF ];
				*s++ = kHexChars[ ( offset >> 16 ) & 0xF ];
			}
			*s++ = kHexChars[ ( offset >> 12 ) & 0xF ];
			*s++ = kHexChars[ ( offset >>  8 ) & 0xF ];
			*s++ = kHexChars[ ( offset >>  4 ) & 0xF ];
			*s++ = kHexChars[   offset         & 0xF ];
		}
		if( s != prefixString )
		{
			*s++ = ':';
			*s++ = ' ';
		}
		check( ( (size_t)( s - prefixString ) ) < sizeof( prefixString ) );
		*s = '\0';
		
		// Build a hex string with a optional spaces after every 1, 2, and/or 4 bytes to make it easier to read.
		// Optionally pads the hex string with space to fill the full 16 byte range (so it lines up).
		
		s = hexString;
		chunkSize = ( inDataSize < 16 ) ? inDataSize : 16;
		n = ( inFlags & kDebugFlagsNo16ByteHexPad ) ? chunkSize : 16;
		for( i = 0; i < n; ++i )
		{
			if( ( inFlags & kDebugFlags8BitSeparator ) && ( i > 0 ) )
			{
				*s++ = ' ';
			}
			if( ( inFlags & kDebugFlags16BitSeparator ) && ( i > 0 ) && ( ( i % 2 ) == 0 ) )
			{
				*s++ = ' ';
			}
			if( !( inFlags & kDebugFlagsNo32BitSeparator ) && ( i > 0 ) && ( ( i % 4 ) == 0 ) )
			{
				*s++ = ' ';
			}
			if( i < chunkSize )
			{
				*s++ = kHexChars[ src[ i ] >> 4   ];
				*s++ = kHexChars[ src[ i ] &  0xF ];
			}
			else
			{
				*s++ = ' ';
				*s++ = ' ';
			}
		}
		check( ( (size_t)( s - hexString ) ) < sizeof( hexString ) );
		*s = '\0';
		
		// Build a string with the ASCII version of the data (replaces non-printable characters with '^').
		// Optionally pads the string with '`' to fill the full 16 byte range (so it lines up).
		
		s = asciiString;
		if( !( inFlags & kDebugFlagsNoASCII ) )
		{
			*s++ = ' ';
			*s++ = '|';
			for( i = 0; i < n; ++i )
			{
				if( i < chunkSize )
				{
					c = src[ i ];
					if( !DebugIsPrint( c ) )
					{
						c = '^';
					}
				}
				else
				{
					c = '`';
				}
				*s++ = (char) c;
			}
			*s++ = '|';
			check( ( (size_t)( s - asciiString ) ) < sizeof( asciiString ) );
		}
		*s = '\0';
		
		// Build a string indicating how bytes are in the hex dump. Only printed on the first line.
		
		s = byteCountString;
		if( !( inFlags & kDebugFlagsNoByteCount ) )
		{
			if( src == start )
			{
				s += DebugSNPrintF( s, sizeof( byteCountString ), " (%d bytes)", (int) inDataSize );
			}
		}
		check( ( (size_t)( s - byteCountString ) ) < sizeof( byteCountString ) );
		*s = '\0';
		
		// Build the entire line from all the pieces we've previously built.
			
		if( outBuffer )
		{
			if( src == start )
			{
				dst += DebugSNPrintF( dst, (size_t)( end - dst ), 
					"%*s"		// Indention
					"%s" 		// Separator (only if needed)
					"%s" 		// Prefix
					"%-*.*s"	// Label
					"%s"		// Separator
					"%s"		// Hex
					"%s"		// ASCII
					"%s"		// Byte Count
					"%s", 		// Newline
					inIndent, "", 
					( src != start ) ? separator : "", 
					prefixString, 
					width, (int) inLabelSize, inLabel ? inLabel : "", 
					( width > 0 ) ? " " : "", 
					hexString, 
					asciiString, 
					byteCountString, 
					newline );
			}
			else
			{
				dst += DebugSNPrintF( dst, (size_t)( end - dst ), 
					"%*s"		// Indention
					"%s" 		// Separator (only if needed)
					"%s" 		// Prefix
					"%*s"		// Label Spacing
					"%s"		// Separator
					"%s"		// Hex
					"%s"		// ASCII
					"%s"		// Byte Count
					"%s", 		// Newline
					inIndent, "", 
					( src != start ) ? separator : "", 
					prefixString, 
					width, "", 
					( width > 0 ) ? " " : "", 
					hexString, 
					asciiString, 
					byteCountString, 
					newline );
			}
		}
		else
		{
			if( src == start )
			{
				dst += DebugPrintF( inLevel, 
					"%*s"		// Indention
					"%s" 		// Separator (only if needed)
					"%s" 		// Prefix
					"%-*.*s"	// Label
					"%s"		// Separator
					"%s"		// Hex
					"%s"		// ASCII
					"%s"		// Byte Count
					"%s", 		// Newline
					inIndent, "", 
					( src != start ) ? separator : "", 
					prefixString, 
					width, (int) inLabelSize, inLabel, 
					( width > 0 ) ? " " : "", 
					hexString, 
					asciiString, 
					byteCountString, 
					newline );
			}
			else
			{
				dst += DebugPrintF( inLevel, 
					"%*s"		// Indention
					"%s" 		// Separator (only if needed)
					"%s" 		// Prefix
					"%*s"		// Label Spacing
					"%s"		// Separator
					"%s"		// Hex
					"%s"		// ASCII
					"%s"		// Byte Count
					"%s", 		// Newline
					inIndent, "", 
					( src != start ) ? separator : "", 
					prefixString, 
					width, "", 
					( width > 0 ) ? " " : "", 
					hexString, 
					asciiString, 
					byteCountString, 
					newline );
			}
		}
		
		// Move to the next chunk. Exit if there is no more data.
		
		offset		+= (int) chunkSize;
		src 		+= chunkSize;
		inDataSize	-= chunkSize;
		if( inDataSize == 0 )
		{
			break;
		}
	}
	
	// Note: The "dst - outBuffer" size calculation works even if "outBuffer" is NULL because it's all relative.
	
	return( (size_t)( dst - outBuffer ) );
}

//===========================================================================================================================
//	DebugNumVersionToString
//===========================================================================================================================

static char *	DebugNumVersionToString( uint32_t inVersion, char *inString )
{
	char *		s;
	uint8_t		majorRev;
	uint8_t		minor;
	uint8_t		bugFix;
	uint8_t		stage;
	uint8_t		revision;
	
	check( inString );
	
	majorRev 	= (uint8_t)( ( inVersion >> 24 ) & 0xFF );
	minor		= (uint8_t)( ( inVersion >> 20 ) & 0x0F );
	bugFix		= (uint8_t)( ( inVersion >> 16 ) & 0x0F );
	stage 		= (uint8_t)( ( inVersion >>  8 ) & 0xFF );
	revision 	= (uint8_t)(   inVersion         & 0xFF );
	
	// Convert the major, minor, and bugfix numbers.
	
	s  = inString;
	s += sprintf( s, "%u", majorRev );
	s += sprintf( s, ".%u", minor );
	if( bugFix != 0 )
	{
		s += sprintf( s, ".%u", bugFix );
	}
	
	// Convert the version stage and non-release revision number.
	
	switch( stage )
	{
		case kVersionStageDevelopment:
			s += sprintf( s, "d%u", revision );
			break;
		
		case kVersionStageAlpha:
			s += sprintf( s, "a%u", revision );
			break;
		
		case kVersionStageBeta:
			s += sprintf( s, "b%u", revision );
			break;
		
		case kVersionStageFinal:
			
			// A non-release revision of zero is a special case indicating the software is GM (at the golden master 
			// stage) and therefore, the non-release revision should not be added to the string.
			
			if( revision != 0 )
			{
				s += sprintf( s, "f%u", revision );
			}
			break;
		
		default:
			dlog( kDebugLevelError, "invalid NumVersion stage (0x%02X)\n", stage );
			break;
	}
	return( inString );
}

//===========================================================================================================================
//	DebugTaskLevel
//===========================================================================================================================

DEBUG_EXPORT uint32_t	DebugTaskLevel( void )
{
	uint32_t		level;
	
	level = 0;
	
#if( TARGET_OS_VXWORKS )
	if( intContext() )
	{
		level |= ( ( 1 << kDebugInterruptLevelShift ) & kDebugInterruptLevelMask );
	}
#endif
	
	return( level );
}

#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
//===========================================================================================================================
//	DebugWinEnableConsole
//===========================================================================================================================

#pragma warning( disable:4311 )

static void	DebugWinEnableConsole( void )
{
	static bool		sConsoleEnabled = false;
	BOOL			result;
	int				fileHandle;
	FILE *			file;
	int				err;
	
	if( sConsoleEnabled )
	{
		goto exit;
	}
	
	// Create console window.
	
	result = AllocConsole();
	require_quiet( result, exit );

	// Redirect stdin to the console stdin.
	
	fileHandle = _open_osfhandle( (long) GetStdHandle( STD_INPUT_HANDLE ), _O_TEXT );
	
	#if( defined( __MWERKS__ ) )
		file = __handle_reopen( (unsigned long) fileHandle, "r", stdin );
		require_quiet( file, exit );
	#else
		file = _fdopen( fileHandle, "r" );
		require_quiet( file, exit );
	
		*stdin = *file;
	#endif
	
	err = setvbuf( stdin, NULL, _IONBF, 0 );
	require_noerr_quiet( err, exit );
	
	// Redirect stdout to the console stdout.
		
	fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
	
	#if( defined( __MWERKS__ ) )
		file = __handle_reopen( (unsigned long) fileHandle, "w", stdout );
		require_quiet( file, exit );
	#else
		file = _fdopen( fileHandle, "w" );
		require_quiet( file, exit );
		
		*stdout = *file;
	#endif
	
	err = setvbuf( stdout, NULL, _IONBF, 0 );
	require_noerr_quiet( err, exit );
	
	// Redirect stderr to the console stdout.
	
	fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
	
	#if( defined( __MWERKS__ ) )
		file = __handle_reopen( (unsigned long) fileHandle, "w", stderr );
		require_quiet( file, exit );
	#else
		file = _fdopen( fileHandle, "w" );
		require_quiet( file, exit );
	
		*stderr = *file;
	#endif
	
	err = setvbuf( stderr, NULL, _IONBF, 0 );
	require_noerr_quiet( err, exit );
	
	sConsoleEnabled = true;
	
exit:
	return;
}

#pragma warning( default:4311 )

#endif	// TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE

#if( TARGET_OS_WIN32 )
//===========================================================================================================================
//	DebugWinCharToTCharString
//===========================================================================================================================

static TCHAR *
	DebugWinCharToTCharString( 
		const char *	inCharString, 
		size_t 			inCharCount, 
		TCHAR *			outTCharString, 
		size_t 			inTCharCountMax, 
		size_t *		outTCharCount )
{
	const char *		src;
	TCHAR *				dst;
	TCHAR *				end;
	
	if( inCharCount == kSizeCString )
	{
		inCharCount = strlen( inCharString );
	}
	src = inCharString;
	dst = outTCharString;
	if( inTCharCountMax > 0 )
	{
		inTCharCountMax -= 1;
		if( inTCharCountMax > inCharCount )
		{
			inTCharCountMax = inCharCount;
		}
		
		end = dst + inTCharCountMax;
		while( dst < end )
		{
			*dst++ = (TCHAR) *src++;
		}
		*dst = 0;
	}
	if( outTCharCount )
	{
		*outTCharCount = (size_t)( dst - outTCharString );
	}
	return( outTCharString );
}
#endif

#if 0
#pragma mark -
#pragma mark == Debugging ==
#endif

//===========================================================================================================================
//	DebugServicesTest
//===========================================================================================================================

DEBUG_EXPORT OSStatus	DebugServicesTest( void )
{
	OSStatus		err;
	char			s[ 512 ];
	uint8_t *		p;
	uint8_t			data[] = 
	{
		0x11, 0x22, 0x33, 0x44, 
		0x55, 0x66, 
		0x77, 0x88, 0x99, 0xAA, 
		0xBB, 0xCC, 0xDD, 
		0xEE,
		0xFF, 
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 
		0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 
		0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1 
	};
	
	debug_initialize( kDebugOutputTypeMetaConsole );	
	
	// check's
	
	check( 0 && "SHOULD SEE: check" );
	check( 1 && "SHOULD *NOT* SEE: check (valid)" );
	check_string( 0, "SHOULD SEE: check_string" );
	check_string( 1, "SHOULD *NOT* SEE: check_string (valid)" );
	check_noerr( -123 );
	check_noerr( 10038 );
	check_noerr( 22 );
	check_noerr( 0 );
	check_noerr_string( -6712, "SHOULD SEE: check_noerr_string" );
	check_noerr_string( 0, "SHOULD *NOT* SEE: check_noerr_string (valid)" );
	check_translated_errno( 0 >= 0 && "SHOULD *NOT* SEE", -384, -999 );
	check_translated_errno( -1 >= 0 && "SHOULD SEE", -384, -999 );
	check_translated_errno( -1 >= 0 && "SHOULD SEE", 0, -999 );
	check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 22, 10 );
	check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10,  5, 10 );
	check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 12,  6 );
	check_ptr_overlap( "SHOULD SEE" ? 12 : 0,  6, 10, 10 );
	check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 10, 10, 10 );
	check_ptr_overlap( "SHOULD *NOT* SEE" ? 22 : 0, 10, 10, 10 );
	check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 20, 10 );
	check_ptr_overlap( "SHOULD *NOT* SEE" ? 20 : 0, 10, 10, 10 );
		
	// require's
	
	require( 0 && "SHOULD SEE", require1 );
	{ err = kResponseErr; goto exit; }
require1:
	require( 1 && "SHOULD *NOT* SEE", require2 );
	goto require2Good;
require2:
	{ err = kResponseErr; goto exit; }
require2Good:
	require_string( 0 && "SHOULD SEE", require3, "SHOULD SEE: require_string" );
	{ err = kResponseErr; goto exit; }
require3:
	require_string( 1 && "SHOULD *NOT* SEE", require4, "SHOULD *NOT* SEE: require_string (valid)" );
	goto require4Good;
require4:
	{ err = kResponseErr; goto exit; }
require4Good:
	require_quiet( 0 && "SHOULD SEE", require5 );
	{ err = kResponseErr; goto exit; }
require5:
	require_quiet( 1 && "SHOULD *NOT* SEE", require6 );
	goto require6Good;
require6:
	{ err = kResponseErr; goto exit; }
require6Good:
	require_noerr( -1, require7 );
	{ err = kResponseErr; goto exit; }
require7:
	require_noerr( 0, require8 );
	goto require8Good;
require8:
	{ err = kResponseErr; goto exit; }
require8Good:
	require_noerr_string( -2, require9, "SHOULD SEE: require_noerr_string");
	{ err = kResponseErr; goto exit; }
require9:
	require_noerr_string( 0, require10, "SHOULD *NOT* SEE: require_noerr_string (valid)" );
	goto require10Good;
require10:
	{ err = kResponseErr; goto exit; }
require10Good:
	require_noerr_action_string( -3, require11, dlog( kDebugLevelMax, "action 1 (expected)\n" ), "require_noerr_action_string" );
	{ err = kResponseErr; goto exit; }
require11:
	require_noerr_action_string( 0, require12, dlog( kDebugLevelMax, "action 2\n" ), "require_noerr_action_string (valid)" );
	goto require12Good;
require12:
	{ err = kResponseErr; goto exit; }
require12Good:
	require_noerr_quiet( -4, require13 );
	{ err = kResponseErr; goto exit; }
require13:
	require_noerr_quiet( 0, require14 );
	goto require14Good;
require14:
	{ err = kResponseErr; goto exit; }
require14Good:
	require_noerr_action( -5, require15, dlog( kDebugLevelMax, "SHOULD SEE: action 3 (expected)\n" ) );
	{ err = kResponseErr; goto exit; }
require15:
	require_noerr_action( 0, require16, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 4\n" ) );
	goto require16Good;
require16:
	{ err = kResponseErr; goto exit; }
require16Good:
	require_noerr_action_quiet( -4, require17, dlog( kDebugLevelMax, "SHOULD SEE: action 5 (expected)\n" ) );
	{ err = kResponseErr; goto exit; }
require17:
	require_noerr_action_quiet( 0, require18, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 6\n" ) );
	goto require18Good;
require18:
	{ err = kResponseErr; goto exit; }
require18Good:
	require_action( 0 && "SHOULD SEE", require19, dlog( kDebugLevelMax, "SHOULD SEE: action 7 (expected)\n" ) );
	{ err = kResponseErr; goto exit; }
require19:
	require_action( 1 && "SHOULD *NOT* SEE", require20, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 8\n" ) );
	goto require20Good;
require20:
	{ err = kResponseErr; goto exit; }
require20Good:
	require_action_quiet( 0, require21, dlog( kDebugLevelMax, "SHOULD SEE: action 9 (expected)\n" ) );
	{ err = kResponseErr; goto exit; }
require21:
	require_action_quiet( 1, require22, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 10\n" ) );
	goto require22Good;
require22:
	{ err = kResponseErr; goto exit; }
require22Good:
	require_action_string( 0, require23, dlog( kDebugLevelMax, "SHOULD SEE: action 11 (expected)\n" ), "SHOULD SEE: require_action_string" );
	{ err = kResponseErr; goto exit; }
require23:
	require_action_string( 1, require24, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 12\n" ), "SHOULD *NOT* SEE: require_action_string" );
	goto require24Good;
require24:
	{ err = kResponseErr; goto exit; }
require24Good:

#if( defined( __MWERKS__ )  )
	#if( defined( __cplusplus ) && __option( exceptions ) )
		#define COMPILER_HAS_EXCEPTIONS		1
	#else
		#define COMPILER_HAS_EXCEPTIONS		0
	#endif
#else
	#if( defined( __cplusplus ) )
		#define COMPILER_HAS_EXCEPTIONS		1
	#else
		#define COMPILER_HAS_EXCEPTIONS		0
	#endif
#endif

#if( COMPILER_HAS_EXCEPTIONS )
	try
	{
		require_throw( 1 && "SHOULD *NOT* SEE" );
		require_throw( 0 && "SHOULD SEE" );
	}
	catch( ... )
	{
		goto require26Good;
	}
	{ err = kResponseErr; goto exit; }
require26Good:
#endif

	// translate_errno
	
	err = translate_errno( 1 != -1, -123, -567 );
	require( ( err == 0 ) && "SHOULD *NOT* SEE", exit );
	
	err = translate_errno( -1 != -1, -123, -567 );
	require( ( err == -123 ) && "SHOULD *NOT* SEE", exit );
	
	err = translate_errno( -1 != -1, 0, -567 );
	require( ( err == -567 ) && "SHOULD *NOT* SEE", exit );

	// debug_string
	
	debug_string( "debug_string" );
	
	// DebugSNPrintF
	
	DebugSNPrintF( s, sizeof( s ), "%d", 1234 );
	require_action( strcmp( s, "1234" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%X", 0x2345 );
	require_action( strcmp( s, "2345" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%#s", "\05test" );
	require_action( strcmp( s, "test" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%##s", "\03www\05apple\03com" );
	require_action( strcmp( s, "www.apple.com." ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%ld", (long) INT32_C( 2147483647 ) );
	require_action( strcmp( s, "2147483647" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%lu", (unsigned long) UINT32_C( 4294967295 ) );
	require_action( strcmp( s, "4294967295" ) == 0, exit, err = -1 );
	
	#if( TYPE_LONGLONG_NATIVE )
		DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( 9223372036854775807 ) );
		require_action( strcmp( s, "9223372036854775807" ) == 0, exit, err = -1 );
		
		DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( -9223372036854775807 ) );
		require_action( strcmp( s, "-9223372036854775807" ) == 0, exit, err = -1 );
		
		DebugSNPrintF( s, sizeof( s ), "%llu", (unsigned_long_long_compat) UINT64_C( 18446744073709551615 ) );
		require_action( strcmp( s, "18446744073709551615" ) == 0, exit, err = -1 );
	#endif
	
	DebugSNPrintF( s, sizeof( s ), "%lb", (unsigned long) binary_32( 01111011, 01111011, 01111011, 01111011 ) );
	require_action( strcmp( s, "1111011011110110111101101111011" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%C", 0x41624364 );	// 'AbCd'
	require_action( strcmp( s, "AbCd" ) == 0, exit, err = -1 );
	
	#if( defined( MDNS_DEBUGMSGS ) )
	{
		mDNSAddr		maddr;
		
		memset( &maddr, 0, sizeof( maddr ) );
		maddr.type = mDNSAddrType_IPv4;
		maddr.ip.v4.b[ 0 ] = 127;
		maddr.ip.v4.b[ 1 ] = 0;
		maddr.ip.v4.b[ 2 ] = 0;
		maddr.ip.v4.b[ 3 ] = 1;
		DebugSNPrintF( s, sizeof( s ), "%#a", &maddr );
		require_action( strcmp( s, "127.0.0.1" ) == 0, exit, err = -1 );
		
		memset( &maddr, 0, sizeof( maddr ) );
		maddr.type = mDNSAddrType_IPv6;
		maddr.ip.v6.b[  0 ]	= 0xFE;
		maddr.ip.v6.b[  1 ]	= 0x80;
		maddr.ip.v6.b[ 15 ]	= 0x01;
		DebugSNPrintF( s, sizeof( s ), "%#a", &maddr );
		require_action( strcmp( s, "FE80:0000:0000:0000:0000:0000:0000:0001" ) == 0, exit, err = -1 );
	}
	#endif
	
	#if( AF_INET )
	{
		struct sockaddr_in		sa4;
		
		memset( &sa4, 0, sizeof( sa4 ) );
		sa4.sin_family 		= AF_INET;
		p 					= (uint8_t *) &sa4.sin_port;
		p[ 0 ] 				= (uint8_t)( ( 80 >> 8 ) & 0xFF );
		p[ 1 ] 				= (uint8_t)(   80        & 0xFF );
		p 					= (uint8_t *) &sa4.sin_addr.s_addr;
		p[ 0 ] 				= (uint8_t)( ( INADDR_LOOPBACK >> 24 ) & 0xFF );
		p[ 1 ] 				= (uint8_t)( ( INADDR_LOOPBACK >> 16 ) & 0xFF );
		p[ 2 ] 				= (uint8_t)( ( INADDR_LOOPBACK >>  8 ) & 0xFF );
		p[ 3 ] 				= (uint8_t)(   INADDR_LOOPBACK         & 0xFF );
		DebugSNPrintF( s, sizeof( s ), "%##a", &sa4 );
		require_action( strcmp( s, "127.0.0.1:80" ) == 0, exit, err = -1 );
	}
	#endif
	
	#if( AF_INET6 )
	{
		struct sockaddr_in6		sa6;
		
		memset( &sa6, 0, sizeof( sa6 ) );
		sa6.sin6_family 			= AF_INET6;
		p 							= (uint8_t *) &sa6.sin6_port;
		p[ 0 ] 						= (uint8_t)( ( 80 >> 8 ) & 0xFF );
		p[ 1 ] 						= (uint8_t)(   80        & 0xFF );
		sa6.sin6_addr.s6_addr[  0 ]	= 0xFE;
		sa6.sin6_addr.s6_addr[  1 ]	= 0x80;
		sa6.sin6_addr.s6_addr[ 15 ]	= 0x01;
		sa6.sin6_scope_id			= 2;
		DebugSNPrintF( s, sizeof( s ), "%##a", &sa6 );
		require_action( strcmp( s, "[FE80:0000:0000:0000:0000:0000:0000:0001%2]:80" ) == 0, exit, err = -1 );
	}
	#endif
	
	// Unicode

	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes" );
	require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "test" );
	require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "testing" );
	require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9" );
	require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9ing" );
	require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes\xC3\xA9ing" );
	require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbf" );
	require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbfing" );
	require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbf" );
	require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbfing" );
	require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 7, "te\xC3\xA9\xed\x9f\xbfing" );
	require_action( strcmp( s, "te\xC3\xA9\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 6, "te\xC3\xA9\xed\x9f\xbfing" );
	require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
	
	DebugSNPrintF(s, sizeof(s), "%.*s", 5, "te\xC3\xA9\xed\x9f\xbfing" );
	require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );

	#if( TARGET_RT_BIG_ENDIAN )
		DebugSNPrintF( s, sizeof( s ), "%S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" );
		require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
	#else
		DebugSNPrintF( s, sizeof( s ), "%S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" );
		require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
	#endif
	
	DebugSNPrintF( s, sizeof( s ), "%S", 
		"\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" );	// Big Endian BOM
	require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%S", 
		"\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" );	// Little Endian BOM
	require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%#S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" );	// Big Endian
	require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%##S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" );	// Little Endian
	require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%.*S", 
		4, "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" );	// Big Endian BOM
	require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%.*S", 
		4, "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" );	// Little Endian BOM
	require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
	
	#if( TARGET_RT_BIG_ENDIAN )
		DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" );
		require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
	#else
		DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" );
		require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
	#endif
	
	DebugSNPrintF( s, sizeof( s ), "%#.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" );	// Big Endian
	require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%##.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" );	// Little Endian
	require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
	
	// Misc
	
	DebugSNPrintF( s, sizeof( s ), "%U", "\x10\xb8\xa7\x6b" "\xad\x9d" "\xd1\x11" "\x80\xb4" "\x00\xc0\x4f\xd4\x30\xc8" );
	require_action( strcmp( s, "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%m", 0 );
	require_action( strcmp( s, "no error" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "%lm", (long) 0 );
	require_action( strcmp( s, "no error" ) == 0, exit, err = -1 );
	
	DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", 16, 16 );
	DebugPrintF( kDebugLevelMax, "%s\n\n", s );
	
	DebugSNPrintF( s, sizeof( s ), "\"%H\"", 
		"\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"
		"\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", 
		32, 32 );
	DebugPrintF( kDebugLevelMax, "%s\n\n", s );
	
	DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7", 2, 2 );
	DebugPrintF( kDebugLevelMax, "%s\n\n", s );
	
	// Hex Dumps
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), 
		kDebugFlagsNone, s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
		kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), 
		kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), 
		kDebugFlagsNoAddress, s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
		kDebugFlagsNoOffset, s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
		kDebugFlagsNoAddress, s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
		kDebugFlagsNoOffset, s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
		kDebugFlagsNoByteCount, s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, "\x41\x62\x43\x64", "\x41\x62\x43\x64", 4,	// 'AbCd'
		kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine |
		kDebugFlagsNo32BitSeparator | kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, 
		s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
		kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoASCII | kDebugFlagsNoNewLine |
		kDebugFlags16BitSeparator | kDebugFlagsNo32BitSeparator |
		kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	s[ 0 ] = '\0';
	DebugHexDump( kDebugLevelMax, 8, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), kDebugFlagsNone, s, sizeof( s ) );
	DebugPrintF( kDebugLevelMax, "%s\n", s );
	
	// dlog's
	
	dlog( kDebugLevelNotice, "dlog\n" );
	dlog( kDebugLevelNotice, "dlog integer: %d\n", 123 );
	dlog( kDebugLevelNotice, "dlog string:  \"%s\"\n", "test string" );
	dlogmem( kDebugLevelNotice, data, sizeof( data ) );
	
	// Done
	
	DebugPrintF( kDebugLevelMax, "\n\nALL TESTS DONE\n\n" );
	err = kNoErr;
	
exit:
	if( err )
	{
		DebugPrintF( kDebugLevelMax, "\n\n### TEST FAILED ###\n\n" );
	}
	return( err );
}

#endif	// DEBUG