/* -*- 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
