/* $Id: strformatrt.cpp $ */
/** @file
 * IPRT - IPRT String Formatter Extensions.
 */

/*
 * Copyright (C) 2006-2015 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
 * VirtualBox OSE distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 */


/*********************************************************************************************************************************
*   Header Files                                                                                                                 *
*********************************************************************************************************************************/
#define LOG_GROUP RTLOGGROUP_STRING
#include <iprt/string.h>
#ifndef RT_NO_EXPORT_SYMBOL
# define RT_NO_EXPORT_SYMBOL /* don't slurp <linux/module.h> which then again
                                slurps arch-specific headers defining symbols */
#endif
#include "internal/iprt.h"

#include <iprt/log.h>
#include <iprt/assert.h>
#include <iprt/string.h>
#include <iprt/stdarg.h>
#ifdef IN_RING3
# include <iprt/thread.h>
# include <iprt/err.h>
#endif
#include <iprt/ctype.h>
#include <iprt/time.h>
#include <iprt/net.h>
#include <iprt/path.h>
#include <iprt/asm.h>
#define STRFORMAT_WITH_X86
#ifdef STRFORMAT_WITH_X86
# include <iprt/x86.h>
#endif
#include "internal/string.h"


/*********************************************************************************************************************************
*   Global Variables                                                                                                             *
*********************************************************************************************************************************/
static char g_szHexDigits[17] = "0123456789abcdef";


/**
 * Helper that formats a 16-bit hex word in a IPv6 address.
 *
 * @returns Length in chars.
 * @param   pszDst      The output buffer.  Written from the start.
 * @param   uWord       The word to format as hex.
 */
static size_t rtstrFormatIPv6HexWord(char *pszDst, uint16_t uWord)
{
    size_t   off;
    uint16_t cDigits;

    if (uWord & UINT16_C(0xff00))
        cDigits = uWord & UINT16_C(0xf000) ? 4 : 3;
    else
        cDigits = uWord & UINT16_C(0x00f0) ? 2 : 1;

    off = 0;
    switch (cDigits)
    {
        case 4: pszDst[off++] = g_szHexDigits[(uWord >> 12) & 0xf];
        case 3: pszDst[off++] = g_szHexDigits[(uWord >>  8) & 0xf];
        case 2: pszDst[off++] = g_szHexDigits[(uWord >>  4) & 0xf];
        case 1: pszDst[off++] = g_szHexDigits[(uWord >>  0) & 0xf];
            break;
    }
    pszDst[off] = '\0';
    return off;
}


/**
 * Helper function to format IPv6 address according to RFC 5952.
 *
 * @returns The number of bytes formatted.
 * @param   pfnOutput       Pointer to output function.
 * @param   pvArgOutput     Argument for the output function.
 * @param   pIpv6Addr       IPv6 address
 */
static size_t rtstrFormatIPv6(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PCRTNETADDRIPV6 pIpv6Addr)
{
    size_t cch; /* result */
    bool   fEmbeddedIpv4;
    size_t cwHexPart;
    size_t cwLongestZeroRun;
    size_t iLongestZeroStart;
    size_t idx;
    char   szHexWord[8];

    Assert(pIpv6Addr != NULL);

    /*
     * Check for embedded IPv4 address.
     *
     * IPv4-compatible - ::11.22.33.44 (obsolete)
     * IPv4-mapped     - ::ffff:11.22.33.44
     * IPv4-translated - ::ffff:0:11.22.33.44 (RFC 2765)
     */
    fEmbeddedIpv4 = false;
    cwHexPart = RT_ELEMENTS(pIpv6Addr->au16);
    if (   pIpv6Addr->au64[0] == 0
        && (   (   pIpv6Addr->au32[2] == 0
                && pIpv6Addr->au32[3] != 0
                && pIpv6Addr->au32[3] != RT_H2BE_U32_C(1) )
            || pIpv6Addr->au32[2] == RT_H2BE_U32_C(0x0000ffff)
            || pIpv6Addr->au32[2] == RT_H2BE_U32_C(0xffff0000) ) )
    {
        fEmbeddedIpv4 = true;
        cwHexPart -= 2;
    }

    /*
     * Find the longest sequences of two or more zero words.
     */
    cwLongestZeroRun  = 0;
    iLongestZeroStart = 0;
    for (idx = 0; idx < cwHexPart; idx++)
        if (pIpv6Addr->au16[idx] == 0)
        {
            size_t iZeroStart = idx;
            size_t cwZeroRun;
            do
                idx++;
            while (idx < cwHexPart && pIpv6Addr->au16[idx] == 0);
            cwZeroRun = idx - iZeroStart;
            if (cwZeroRun > 1 && cwZeroRun > cwLongestZeroRun)
            {
                cwLongestZeroRun  = cwZeroRun;
                iLongestZeroStart = iZeroStart;
                if (cwZeroRun >= cwHexPart - idx)
                    break;
            }
        }

    /*
     * Do the formatting.
     */
    cch = 0;
    if (cwLongestZeroRun == 0)
    {
        for (idx = 0; idx < cwHexPart; ++idx)
        {
            if (idx > 0)
                cch += pfnOutput(pvArgOutput, ":", 1);
            cch += pfnOutput(pvArgOutput, szHexWord, rtstrFormatIPv6HexWord(szHexWord, RT_BE2H_U16(pIpv6Addr->au16[idx])));
        }

        if (fEmbeddedIpv4)
            cch += pfnOutput(pvArgOutput, ":", 1);
    }
    else
    {
        const size_t iLongestZeroEnd = iLongestZeroStart + cwLongestZeroRun;

        if (iLongestZeroStart == 0)
            cch += pfnOutput(pvArgOutput, ":", 1);
        else
            for (idx = 0; idx < iLongestZeroStart; ++idx)
            {
                cch += pfnOutput(pvArgOutput, szHexWord, rtstrFormatIPv6HexWord(szHexWord, RT_BE2H_U16(pIpv6Addr->au16[idx])));
                cch += pfnOutput(pvArgOutput, ":", 1);
            }

        if (iLongestZeroEnd == cwHexPart)
            cch += pfnOutput(pvArgOutput, ":", 1);
        else
        {
            for (idx = iLongestZeroEnd; idx < cwHexPart; ++idx)
            {
                cch += pfnOutput(pvArgOutput, ":", 1);
                cch += pfnOutput(pvArgOutput, szHexWord, rtstrFormatIPv6HexWord(szHexWord, RT_BE2H_U16(pIpv6Addr->au16[idx])));
            }

            if (fEmbeddedIpv4)
                cch += pfnOutput(pvArgOutput, ":", 1);
        }
    }

    if (fEmbeddedIpv4)
        cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
                           "%u.%u.%u.%u",
                           pIpv6Addr->au8[12],
                           pIpv6Addr->au8[13],
                           pIpv6Addr->au8[14],
                           pIpv6Addr->au8[15]);

    return cch;
}


/**
 * Callback to format iprt formatting extentions.
 * See @ref pg_rt_str_format for a reference on the format types.
 *
 * @returns The number of bytes formatted.
 * @param   pfnOutput       Pointer to output function.
 * @param   pvArgOutput     Argument for the output function.
 * @param   ppszFormat      Pointer to the format string pointer. Advance this till the char
 *                          after the format specifier.
 * @param   pArgs           Pointer to the argument list. Use this to fetch the arguments.
 * @param   cchWidth        Format Width. -1 if not specified.
 * @param   cchPrecision    Format Precision. -1 if not specified.
 * @param   fFlags          Flags (RTSTR_NTFS_*).
 * @param   chArgSize       The argument size specifier, 'l' or 'L'.
 */
DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, const char **ppszFormat, va_list *pArgs,
                                 int cchWidth, int cchPrecision, unsigned fFlags, char chArgSize)
{
    const char *pszFormatOrg = *ppszFormat;
    char        ch = *(*ppszFormat)++;
    size_t      cch;
    char        szBuf[80];

    if (ch == 'R')
    {
        ch = *(*ppszFormat)++;
        switch (ch)
        {
            /*
             * Groups 1 and 2.
             */
            case 'T':
            case 'G':
            case 'H':
            case 'R':
            case 'C':
            case 'I':
            case 'X':
            case 'U':
            {
                /*
                 * Interpret the type.
                 */
                typedef enum
                {
                    RTSF_INT,
                    RTSF_INTW,
                    RTSF_BOOL,
                    RTSF_FP16,
                    RTSF_FP32,
                    RTSF_FP64,
                    RTSF_IPV4,
                    RTSF_IPV6,
                    RTSF_MAC,
                    RTSF_NETADDR,
                    RTSF_UUID
                } RTSF;
                static const struct
                {
                    uint8_t     cch;        /**< the length of the string. */
                    char        sz[10];     /**< the part following 'R'. */
                    uint8_t     cb;         /**< the size of the type. */
                    uint8_t     u8Base;     /**< the size of the type. */
                    RTSF        enmFormat;  /**< The way to format it. */
                    uint16_t    fFlags;     /**< additional RTSTR_F_* flags. */
                }
                /** Sorted array of types, looked up using binary search! */
                s_aTypes[] =
                {
#define STRMEM(str) sizeof(str) - 1, str
                    { STRMEM("Ci"),      sizeof(RTINT),          10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("Cp"),      sizeof(RTCCPHYS),       16, RTSF_INTW,  0 },
                    { STRMEM("Cr"),      sizeof(RTCCUINTREG),    16, RTSF_INTW,  0 },
                    { STRMEM("Cu"),      sizeof(RTUINT),         10, RTSF_INT,   0 },
                    { STRMEM("Cv"),      sizeof(void *),         16, RTSF_INTW,  0 },
                    { STRMEM("Cx"),      sizeof(RTUINT),         16, RTSF_INT,   0 },
                    { STRMEM("Gi"),      sizeof(RTGCINT),        10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("Gp"),      sizeof(RTGCPHYS),       16, RTSF_INTW,  0 },
                    { STRMEM("Gr"),      sizeof(RTGCUINTREG),    16, RTSF_INTW,  0 },
                    { STRMEM("Gu"),      sizeof(RTGCUINT),       10, RTSF_INT,   0 },
                    { STRMEM("Gv"),      sizeof(RTGCPTR),        16, RTSF_INTW,  0 },
                    { STRMEM("Gx"),      sizeof(RTGCUINT),       16, RTSF_INT,   0 },
                    { STRMEM("Hi"),      sizeof(RTHCINT),        10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("Hp"),      sizeof(RTHCPHYS),       16, RTSF_INTW,  0 },
                    { STRMEM("Hr"),      sizeof(RTHCUINTREG),    16, RTSF_INTW,  0 },
                    { STRMEM("Hu"),      sizeof(RTHCUINT),       10, RTSF_INT,   0 },
                    { STRMEM("Hv"),      sizeof(RTHCPTR),        16, RTSF_INTW,  0 },
                    { STRMEM("Hx"),      sizeof(RTHCUINT),       16, RTSF_INT,   0 },
                    { STRMEM("I16"),     sizeof(int16_t),        10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("I32"),     sizeof(int32_t),        10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("I64"),     sizeof(int64_t),        10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("I8"),      sizeof(int8_t),         10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("Rv"),      sizeof(RTRCPTR),        16, RTSF_INTW,  0 },
                    { STRMEM("Tbool"),   sizeof(bool),           10, RTSF_BOOL,  0 },
                    { STRMEM("Tfile"),   sizeof(RTFILE),         10, RTSF_INT,   0 },
                    { STRMEM("Tfmode"),  sizeof(RTFMODE),        16, RTSF_INTW,  0 },
                    { STRMEM("Tfoff"),   sizeof(RTFOFF),         10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("Tfp16"),   sizeof(RTFAR16),        16, RTSF_FP16,  RTSTR_F_ZEROPAD },
                    { STRMEM("Tfp32"),   sizeof(RTFAR32),        16, RTSF_FP32,  RTSTR_F_ZEROPAD },
                    { STRMEM("Tfp64"),   sizeof(RTFAR64),        16, RTSF_FP64,  RTSTR_F_ZEROPAD },
                    { STRMEM("Tgid"),    sizeof(RTGID),          10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("Tino"),    sizeof(RTINODE),        16, RTSF_INTW,  0 },
                    { STRMEM("Tint"),    sizeof(RTINT),          10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("Tiop"),    sizeof(RTIOPORT),       16, RTSF_INTW,  0 },
                    { STRMEM("Tldrm"),   sizeof(RTLDRMOD),       16, RTSF_INTW,  0 },
                    { STRMEM("Tmac"),    sizeof(PCRTMAC),        16, RTSF_MAC,   0 },
                    { STRMEM("Tnaddr"),  sizeof(PCRTNETADDR),    10, RTSF_NETADDR,0 },
                    { STRMEM("Tnaipv4"), sizeof(RTNETADDRIPV4),  10, RTSF_IPV4,  0 },
                    { STRMEM("Tnaipv6"), sizeof(PCRTNETADDRIPV6),16, RTSF_IPV6,  0 },
                    { STRMEM("Tnthrd"),  sizeof(RTNATIVETHREAD), 16, RTSF_INTW,  0 },
                    { STRMEM("Tproc"),   sizeof(RTPROCESS),      16, RTSF_INTW,  0 },
                    { STRMEM("Tptr"),    sizeof(RTUINTPTR),      16, RTSF_INTW,  0 },
                    { STRMEM("Treg"),    sizeof(RTCCUINTREG),    16, RTSF_INTW,  0 },
                    { STRMEM("Tsel"),    sizeof(RTSEL),          16, RTSF_INTW,  0 },
                    { STRMEM("Tsem"),    sizeof(RTSEMEVENT),     16, RTSF_INTW,  0 },
                    { STRMEM("Tsock"),   sizeof(RTSOCKET),       10, RTSF_INT,   0 },
                    { STRMEM("Tthrd"),   sizeof(RTTHREAD),       16, RTSF_INTW,  0 },
                    { STRMEM("Tuid"),    sizeof(RTUID),          10, RTSF_INT,   RTSTR_F_VALSIGNED },
                    { STRMEM("Tuint"),   sizeof(RTUINT),         10, RTSF_INT,   0 },
                    { STRMEM("Tunicp"),  sizeof(RTUNICP),        16, RTSF_INTW,  RTSTR_F_ZEROPAD },
                    { STRMEM("Tutf16"),  sizeof(RTUTF16),        16, RTSF_INTW,  RTSTR_F_ZEROPAD },
                    { STRMEM("Tuuid"),   sizeof(PCRTUUID),       16, RTSF_UUID,  0 },
                    { STRMEM("Txint"),   sizeof(RTUINT),         16, RTSF_INT,   0 },
                    { STRMEM("U16"),     sizeof(uint16_t),       10, RTSF_INT,   0 },
                    { STRMEM("U32"),     sizeof(uint32_t),       10, RTSF_INT,   0 },
                    { STRMEM("U64"),     sizeof(uint64_t),       10, RTSF_INT,   0 },
                    { STRMEM("U8"),      sizeof(uint8_t),        10, RTSF_INT,   0 },
                    { STRMEM("X16"),     sizeof(uint16_t),       16, RTSF_INT,   0 },
                    { STRMEM("X32"),     sizeof(uint32_t),       16, RTSF_INT,   0 },
                    { STRMEM("X64"),     sizeof(uint64_t),       16, RTSF_INT,   0 },
                    { STRMEM("X8"),      sizeof(uint8_t),        16, RTSF_INT,   0 },
#undef STRMEM
                };
                static const char s_szNull[] = "<NULL>";

                const char *pszType = *ppszFormat - 1;
                int         iStart  = 0;
                int         iEnd    = RT_ELEMENTS(s_aTypes) - 1;
                int         i       = RT_ELEMENTS(s_aTypes) / 2;

                union
                {
                    uint8_t             u8;
                    uint16_t            u16;
                    uint32_t            u32;
                    uint64_t            u64;
                    int8_t              i8;
                    int16_t             i16;
                    int32_t             i32;
                    int64_t             i64;
                    RTFAR16             fp16;
                    RTFAR32             fp32;
                    RTFAR64             fp64;
                    bool                fBool;
                    PCRTMAC             pMac;
                    RTNETADDRIPV4       Ipv4Addr;
                    PCRTNETADDRIPV6     pIpv6Addr;
                    PCRTNETADDR         pNetAddr;
                    PCRTUUID            pUuid;
                } u;

                AssertMsg(!chArgSize, ("Not argument size '%c' for RT types! '%.10s'\n", chArgSize, pszFormatOrg));

                /*
                 * Lookup the type - binary search.
                 */
                for (;;)
                {
                    int iDiff = strncmp(pszType, s_aTypes[i].sz, s_aTypes[i].cch);
                    if (!iDiff)
                        break;
                    if (iEnd == iStart)
                    {
                        AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg));
                        return 0;
                    }
                    if (iDiff < 0)
                        iEnd = i - 1;
                    else
                        iStart = i + 1;
                    if (iEnd < iStart)
                    {
                        AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg));
                        return 0;
                    }
                    i = iStart + (iEnd - iStart) / 2;
                }

                /*
                 * Advance the format string and merge flags.
                 */
                *ppszFormat += s_aTypes[i].cch - 1;
                fFlags |= s_aTypes[i].fFlags;

                /*
                 * Fetch the argument.
                 * It's important that a signed value gets sign-extended up to 64-bit.
                 */
                RT_ZERO(u);
                if (fFlags & RTSTR_F_VALSIGNED)
                {
                    switch (s_aTypes[i].cb)
                    {
                        case sizeof(int8_t):
                            u.i64 = va_arg(*pArgs, /*int8_t*/int);
                            fFlags |= RTSTR_F_8BIT;
                            break;
                        case sizeof(int16_t):
                            u.i64 = va_arg(*pArgs, /*int16_t*/int);
                            fFlags |= RTSTR_F_16BIT;
                            break;
                        case sizeof(int32_t):
                            u.i64 = va_arg(*pArgs, int32_t);
                            fFlags |= RTSTR_F_32BIT;
                            break;
                        case sizeof(int64_t):
                            u.i64 = va_arg(*pArgs, int64_t);
                            fFlags |= RTSTR_F_64BIT;
                            break;
                        default:
                            AssertMsgFailed(("Invalid format error, size %d'!\n", s_aTypes[i].cb));
                            break;
                    }
                }
                else
                {
                    switch (s_aTypes[i].cb)
                    {
                        case sizeof(uint8_t):
                            u.u8 = va_arg(*pArgs, /*uint8_t*/unsigned);
                            fFlags |= RTSTR_F_8BIT;
                            break;
                        case sizeof(uint16_t):
                            u.u16 = va_arg(*pArgs, /*uint16_t*/unsigned);
                            fFlags |= RTSTR_F_16BIT;
                            break;
                        case sizeof(uint32_t):
                            u.u32 = va_arg(*pArgs, uint32_t);
                            fFlags |= RTSTR_F_32BIT;
                            break;
                        case sizeof(uint64_t):
                            u.u64 = va_arg(*pArgs, uint64_t);
                            fFlags |= RTSTR_F_64BIT;
                            break;
                        case sizeof(RTFAR32):
                            u.fp32 = va_arg(*pArgs, RTFAR32);
                            break;
                        case sizeof(RTFAR64):
                            u.fp64 = va_arg(*pArgs, RTFAR64);
                            break;
                        default:
                            AssertMsgFailed(("Invalid format error, size %d'!\n", s_aTypes[i].cb));
                            break;
                    }
                }

                /*
                 * Format the output.
                 */
                switch (s_aTypes[i].enmFormat)
                {
                    case RTSF_INT:
                    {
                        cch = RTStrFormatNumber(szBuf, u.u64, s_aTypes[i].u8Base, cchWidth, cchPrecision, fFlags);
                        break;
                    }

                    /* hex which defaults to max width. */
                    case RTSF_INTW:
                    {
                        Assert(s_aTypes[i].u8Base == 16);
                        if (cchWidth < 0)
                        {
                            cchWidth = s_aTypes[i].cb * 2 + (fFlags & RTSTR_F_SPECIAL ? 2 : 0);
                            fFlags |= RTSTR_F_ZEROPAD;
                        }
                        cch = RTStrFormatNumber(szBuf, u.u64, s_aTypes[i].u8Base, cchWidth, cchPrecision, fFlags);
                        break;
                    }

                    case RTSF_BOOL:
                    {
                        static const char s_szTrue[]  = "true ";
                        static const char s_szFalse[] = "false";
                        if (u.u64 == 1)
                            return pfnOutput(pvArgOutput, s_szTrue,  sizeof(s_szTrue) - 1);
                        if (u.u64 == 0)
                            return pfnOutput(pvArgOutput, s_szFalse, sizeof(s_szFalse) - 1);
                        /* invalid boolean value */
                        return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "!%lld!", u.u64);
                    }

                    case RTSF_FP16:
                    {
                        fFlags &= ~(RTSTR_F_VALSIGNED | RTSTR_F_BIT_MASK | RTSTR_F_WIDTH | RTSTR_F_PRECISION | RTSTR_F_THOUSAND_SEP);
                        cch = RTStrFormatNumber(&szBuf[0], u.fp16.sel, 16, 4, -1, fFlags | RTSTR_F_16BIT);
                        Assert(cch == 4);
                        szBuf[4] = ':';
                        cch = RTStrFormatNumber(&szBuf[5], u.fp16.off, 16, 4, -1, fFlags | RTSTR_F_16BIT);
                        Assert(cch == 4);
                        cch = 4 + 1 + 4;
                        break;
                    }
                    case RTSF_FP32:
                    {
                        fFlags &= ~(RTSTR_F_VALSIGNED | RTSTR_F_BIT_MASK | RTSTR_F_WIDTH | RTSTR_F_PRECISION | RTSTR_F_THOUSAND_SEP);
                        cch = RTStrFormatNumber(&szBuf[0], u.fp32.sel, 16, 4, -1, fFlags | RTSTR_F_16BIT);
                        Assert(cch == 4);
                        szBuf[4] = ':';
                        cch = RTStrFormatNumber(&szBuf[5], u.fp32.off, 16, 8, -1, fFlags | RTSTR_F_32BIT);
                        Assert(cch == 8);
                        cch = 4 + 1 + 8;
                        break;
                    }
                    case RTSF_FP64:
                    {
                        fFlags &= ~(RTSTR_F_VALSIGNED | RTSTR_F_BIT_MASK | RTSTR_F_WIDTH | RTSTR_F_PRECISION | RTSTR_F_THOUSAND_SEP);
                        cch = RTStrFormatNumber(&szBuf[0], u.fp64.sel, 16, 4, -1, fFlags | RTSTR_F_16BIT);
                        Assert(cch == 4);
                        szBuf[4] = ':';
                        cch = RTStrFormatNumber(&szBuf[5], u.fp64.off, 16, 16, -1, fFlags | RTSTR_F_64BIT);
                        Assert(cch == 16);
                        cch = 4 + 1 + 16;
                        break;
                    }

                    case RTSF_IPV4:
                        return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
                                           "%u.%u.%u.%u",
                                           u.Ipv4Addr.au8[0],
                                           u.Ipv4Addr.au8[1],
                                           u.Ipv4Addr.au8[2],
                                           u.Ipv4Addr.au8[3]);

                    case RTSF_IPV6:
                    {
                        if (VALID_PTR(u.pIpv6Addr))
                            return rtstrFormatIPv6(pfnOutput, pvArgOutput, u.pIpv6Addr);
                        return pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1);
                    }

                    case RTSF_MAC:
                    {
                        if (VALID_PTR(u.pMac))
                            return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
                                               "%02x:%02x:%02x:%02x:%02x:%02x",
                                               u.pMac->au8[0],
                                               u.pMac->au8[1],
                                               u.pMac->au8[2],
                                               u.pMac->au8[3],
                                               u.pMac->au8[4],
                                               u.pMac->au8[5]);
                        return pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1);
                    }

                    case RTSF_NETADDR:
                    {
                        if (VALID_PTR(u.pNetAddr))
                        {
                            switch (u.pNetAddr->enmType)
                            {
                                case RTNETADDRTYPE_IPV4:
                                    if (u.pNetAddr->uPort == RTNETADDR_PORT_NA)
                                        return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
                                                           "%u.%u.%u.%u",
                                                           u.pNetAddr->uAddr.IPv4.au8[0],
                                                           u.pNetAddr->uAddr.IPv4.au8[1],
                                                           u.pNetAddr->uAddr.IPv4.au8[2],
                                                           u.pNetAddr->uAddr.IPv4.au8[3]);
                                    return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
                                                       "%u.%u.%u.%u:%u",
                                                       u.pNetAddr->uAddr.IPv4.au8[0],
                                                       u.pNetAddr->uAddr.IPv4.au8[1],
                                                       u.pNetAddr->uAddr.IPv4.au8[2],
                                                       u.pNetAddr->uAddr.IPv4.au8[3],
                                                       u.pNetAddr->uPort);

                                case RTNETADDRTYPE_IPV6:
                                    if (u.pNetAddr->uPort == RTNETADDR_PORT_NA)
                                        return rtstrFormatIPv6(pfnOutput, pvArgOutput, &u.pNetAddr->uAddr.IPv6);

                                    return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
                                                       "[%RTnaipv6]:%u",
                                                       &u.pNetAddr->uAddr.IPv6,
                                                       u.pNetAddr->uPort);

                                case RTNETADDRTYPE_MAC:
                                    return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
                                                       "%02x:%02x:%02x:%02x:%02x:%02x",
                                                       u.pNetAddr->uAddr.Mac.au8[0],
                                                       u.pNetAddr->uAddr.Mac.au8[1],
                                                       u.pNetAddr->uAddr.Mac.au8[2],
                                                       u.pNetAddr->uAddr.Mac.au8[3],
                                                       u.pNetAddr->uAddr.Mac.au8[4],
                                                       u.pNetAddr->uAddr.Mac.au8[5]);

                                default:
                                    return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
                                                       "unsupported-netaddr-type=%u", u.pNetAddr->enmType);

                            }
                        }
                        return pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1);
                    }

                    case RTSF_UUID:
                    {
                        if (VALID_PTR(u.pUuid))
                        {
                            /* cannot call RTUuidToStr because of GC/R0. */
                            return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
                                               "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
                                               RT_H2LE_U32(u.pUuid->Gen.u32TimeLow),
                                               RT_H2LE_U16(u.pUuid->Gen.u16TimeMid),
                                               RT_H2LE_U16(u.pUuid->Gen.u16TimeHiAndVersion),
                                               u.pUuid->Gen.u8ClockSeqHiAndReserved,
                                               u.pUuid->Gen.u8ClockSeqLow,
                                               u.pUuid->Gen.au8Node[0],
                                               u.pUuid->Gen.au8Node[1],
                                               u.pUuid->Gen.au8Node[2],
                                               u.pUuid->Gen.au8Node[3],
                                               u.pUuid->Gen.au8Node[4],
                                               u.pUuid->Gen.au8Node[5]);
                        }
                        return pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1);
                    }

                    default:
                        AssertMsgFailed(("Internal error %d\n", s_aTypes[i].enmFormat));
                        return 0;
                }

                /*
                 * Finally, output the formatted string and return.
                 */
                return pfnOutput(pvArgOutput, szBuf, cch);
            }


            /* Group 3 */

            /*
             * Base name printing.
             */
            case 'b':
            {
                switch (*(*ppszFormat)++)
                {
                    case 'n':
                    {
                        const char *pszLastSep;
                        const char *psz = pszLastSep = va_arg(*pArgs, const char *);
                        if (!VALID_PTR(psz))
                            return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>"));

                        while ((ch = *psz) != '\0')
                        {
                            if (RTPATH_IS_SEP(ch))
                            {
                                do
                                    psz++;
                                while ((ch = *psz) != '\0' && RTPATH_IS_SEP(ch));
                                if (!ch)
                                    break;
                                pszLastSep = psz;
                            }
                            psz++;
                        }

                        return pfnOutput(pvArgOutput, pszLastSep, psz - pszLastSep);
                    }

                    default:
                        AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
                        break;
                }
                break;
            }


            /*
             * Pretty function / method name printing.
             */
            case 'f':
            {
                switch (*(*ppszFormat)++)
                {
                    /*
                     * Pretty function / method name printing.
                     * This isn't 100% right (see classic signal prototype) and it assumes
                     * standardized names, but it'll do for today.
                     */
                    case 'n':
                    {
                        const char *pszStart;
                        const char *psz = pszStart = va_arg(*pArgs, const char *);
                        if (!VALID_PTR(psz))
                            return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>"));

                        while ((ch = *psz) != '\0' && ch != '(')
                        {
                            if (RT_C_IS_BLANK(ch))
                            {
                                psz++;
                                while ((ch = *psz) != '\0' && (RT_C_IS_BLANK(ch) || ch == '('))
                                    psz++;
                                if (ch)
                                    pszStart = psz;
                            }
                            else if (ch == '(')
                                break;
                            else
                                psz++;
                        }

                        return pfnOutput(pvArgOutput, pszStart, psz - pszStart);
                    }

                    default:
                        AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
                        break;
                }
                break;
            }


            /*
             * hex dumping and COM/XPCOM.
             */
            case 'h':
            {
                switch (*(*ppszFormat)++)
                {
                    /*
                     * Hex stuff.
                     */
                    case 'x':
                    {
                        uint8_t *pu8 = va_arg(*pArgs, uint8_t *);
                        if (cchPrecision < 0)
                            cchPrecision = 16;
                        if (pu8)
                        {
                            switch (*(*ppszFormat)++)
                            {
                                /*
                                 * Regular hex dump.
                                 */
                                case 'd':
                                {
                                    int off = 0;
                                    cch = 0;

                                    if (cchWidth <= 0)
                                        cchWidth = 16;

                                    while (off < cchPrecision)
                                    {
                                        int i;
                                        cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s%0*p %04x:", off ? "\n" : "", sizeof(pu8) * 2, (uintptr_t)pu8, off);
                                        for (i = 0; i < cchWidth && off + i < cchPrecision ; i++)
                                            cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
                                                               off + i < cchPrecision ? !(i & 7) && i ? "-%02x" : " %02x" : "   ", pu8[i]);
                                        while (i++ < cchWidth)
                                            cch += pfnOutput(pvArgOutput, "   ", 3);

                                        cch += pfnOutput(pvArgOutput, " ", 1);

                                        for (i = 0; i < cchWidth && off + i < cchPrecision; i++)
                                        {
                                            uint8_t u8 = pu8[i];
                                            cch += pfnOutput(pvArgOutput, u8 < 127 && u8 >= 32 ? (const char *)&u8 : ".", 1);
                                        }

                                        /* next */
                                        pu8 += cchWidth;
                                        off += cchWidth;
                                    }
                                    return cch;
                                }

                                /*
                                 * Hex string.
                                 */
                                case 's':
                                {
                                    if (cchPrecision-- > 0)
                                    {
                                        cch = RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%02x", *pu8++);
                                        for (; cchPrecision > 0; cchPrecision--, pu8++)
                                            cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, " %02x", *pu8);
                                        return cch;
                                    }
                                    break;
                                }

                                default:
                                    AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
                                    break;
                            }
                        }
                        else
                            return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>"));
                        break;
                    }


#ifdef IN_RING3
                    /*
                     * XPCOM / COM status code: %Rhrc, %Rhrf, %Rhra
                     * ASSUMES: If Windows Then COM else XPCOM.
                     */
                    case 'r':
                    {
                        uint32_t hrc = va_arg(*pArgs, uint32_t);
                        PCRTCOMERRMSG pMsg = RTErrCOMGet(hrc);
                        switch (*(*ppszFormat)++)
                        {
                            case 'c':
                                return pfnOutput(pvArgOutput, pMsg->pszDefine, strlen(pMsg->pszDefine));
                            case 'f':
                                return pfnOutput(pvArgOutput, pMsg->pszMsgFull,strlen(pMsg->pszMsgFull));
                            case 'a':
                                return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s (0x%08X) - %s", pMsg->pszDefine, hrc, pMsg->pszMsgFull);
                            default:
                                AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
                                return 0;
                        }
                        break;
                    }
#endif /* IN_RING3 */

                    default:
                        AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
                        return 0;

                }
                break;
            }

            /*
             * iprt status code: %Rrc, %Rrs, %Rrf, %Rra.
             */
            case 'r':
            {
                int rc = va_arg(*pArgs, int);
#ifdef IN_RING3                         /* we don't want this anywhere else yet. */
                PCRTSTATUSMSG pMsg = RTErrGet(rc);
                switch (*(*ppszFormat)++)
                {
                    case 'c':
                        return pfnOutput(pvArgOutput, pMsg->pszDefine,    strlen(pMsg->pszDefine));
                    case 's':
                        return pfnOutput(pvArgOutput, pMsg->pszMsgShort,  strlen(pMsg->pszMsgShort));
                    case 'f':
                        return pfnOutput(pvArgOutput, pMsg->pszMsgFull,   strlen(pMsg->pszMsgFull));
                    case 'a':
                        return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s (%d) - %s", pMsg->pszDefine, rc, pMsg->pszMsgFull);
                    default:
                        AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
                        return 0;
                }
#else /* !IN_RING3 */
                switch (*(*ppszFormat)++)
                {
                    case 'c':
                    case 's':
                    case 'f':
                    case 'a':
                        return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%d", rc);
                    default:
                        AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
                        return 0;
                }
#endif /* !IN_RING3 */
                break;
            }

#if defined(IN_RING3)
            /*
             * Windows status code: %Rwc, %Rwf, %Rwa
             */
            case 'w':
            {
                long rc = va_arg(*pArgs, long);
# if defined(RT_OS_WINDOWS)
                PCRTWINERRMSG pMsg = RTErrWinGet(rc);
# endif
                switch (*(*ppszFormat)++)
                {
# if defined(RT_OS_WINDOWS)
                    case 'c':
                        return pfnOutput(pvArgOutput, pMsg->pszDefine, strlen(pMsg->pszDefine));
                    case 'f':
                        return pfnOutput(pvArgOutput, pMsg->pszMsgFull,strlen(pMsg->pszMsgFull));
                    case 'a':
                        return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s (0x%08X) - %s", pMsg->pszDefine, rc, pMsg->pszMsgFull);
# else
                    case 'c':
                    case 'f':
                    case 'a':
                        return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "0x%08X", rc);
# endif
                    default:
                        AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
                        return 0;
                }
                break;
            }
#endif /* IN_RING3 */

            /*
             * Group 4, structure dumpers.
             */
            case 'D':
            {
                /*
                 * Interpret the type.
                 */
                typedef enum
                {
                    RTST_TIMESPEC
                } RTST;
/** Set if it's a pointer */
#define RTST_FLAGS_POINTER  RT_BIT(0)
                static const struct
                {
                    uint8_t     cch;        /**< the length of the string. */
                    char        sz[16-2];   /**< the part following 'R'. */
                    uint8_t     cb;         /**< the size of the argument. */
                    uint8_t     fFlags;     /**< RTST_FLAGS_* */
                    RTST        enmType;    /**< The structure type. */
                }
                /** Sorted array of types, looked up using binary search! */
                s_aTypes[] =
                {
#define STRMEM(str) sizeof(str) - 1, str
                    { STRMEM("Dtimespec"),   sizeof(PCRTTIMESPEC),  RTST_FLAGS_POINTER, RTST_TIMESPEC},
#undef STRMEM
                };
                const char *pszType = *ppszFormat - 1;
                int         iStart  = 0;
                int         iEnd    = RT_ELEMENTS(s_aTypes) - 1;
                int         i       = RT_ELEMENTS(s_aTypes) / 2;

                union
                {
                    const void     *pv;
                    uint64_t        u64;
                    PCRTTIMESPEC    pTimeSpec;
                } u;

                AssertMsg(!chArgSize, ("Not argument size '%c' for RT types! '%.10s'\n", chArgSize, pszFormatOrg));

                /*
                 * Lookup the type - binary search.
                 */
                for (;;)
                {
                    int iDiff = strncmp(pszType, s_aTypes[i].sz, s_aTypes[i].cch);
                    if (!iDiff)
                        break;
                    if (iEnd == iStart)
                    {
                        AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg));
                        return 0;
                    }
                    if (iDiff < 0)
                        iEnd = i - 1;
                    else
                        iStart = i + 1;
                    if (iEnd < iStart)
                    {
                        AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg));
                        return 0;
                    }
                    i = iStart + (iEnd - iStart) / 2;
                }
                *ppszFormat += s_aTypes[i].cch - 1;

                /*
                 * Fetch the argument.
                 */
                u.u64 = 0;
                switch (s_aTypes[i].cb)
                {
                    case sizeof(const void *):
                        u.pv = va_arg(*pArgs, const void *);
                        break;
                    default:
                        AssertMsgFailed(("Invalid format error, size %d'!\n", s_aTypes[i].cb));
                        break;
                }

                /*
                 * If it's a pointer, we'll check if it's valid before going on.
                 */
                if ((s_aTypes[i].fFlags & RTST_FLAGS_POINTER) && !VALID_PTR(u.pv))
                    return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>"));

                /*
                 * Format the output.
                 */
                switch (s_aTypes[i].enmType)
                {
                    case RTST_TIMESPEC:
                        return RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL, "%'lld ns", RTTimeSpecGetNano(u.pTimeSpec));

                    default:
                        AssertMsgFailed(("Invalid/unhandled enmType=%d\n", s_aTypes[i].enmType));
                        break;
                }
                break;
            }

#ifdef IN_RING3
            /*
             * Group 5, XML / HTML escapers.
             */
            case 'M':
            {
                char chWhat = (*ppszFormat)[0];
                bool fAttr  = chWhat == 'a';
                char chType = (*ppszFormat)[1];
                AssertMsgBreak(chWhat == 'a' || chWhat == 'e', ("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));
                *ppszFormat += 2;
                switch (chType)
                {
                    case 's':
                    {
                        static const char   s_szElemEscape[] = "<>&\"'";
                        static const char   s_szAttrEscape[] = "<>&\"\n\r"; /* more? */
                        const char * const  pszEscape =  fAttr ?             s_szAttrEscape  :             s_szElemEscape;
                        size_t       const  cchEscape = (fAttr ? RT_ELEMENTS(s_szAttrEscape) : RT_ELEMENTS(s_szElemEscape)) - 1;
                        size_t      cchOutput = 0;
                        const char *pszStr    = va_arg(*pArgs, char *);
                        ssize_t     cchStr;
                        ssize_t     offCur;
                        ssize_t     offLast;

                        if (!VALID_PTR(pszStr))
                            pszStr = "<NULL>";
                        cchStr = RTStrNLen(pszStr, (unsigned)cchPrecision);

                        if (fAttr)
                            cchOutput += pfnOutput(pvArgOutput, "\"", 1);
                        if (!(fFlags & RTSTR_F_LEFT))
                            while (--cchWidth >= cchStr)
                                cchOutput += pfnOutput(pvArgOutput, " ", 1);

                        offLast = offCur = 0;
                        while (offCur < cchStr)
                        {
                            if (memchr(pszEscape, pszStr[offCur], cchEscape))
                            {
                                if (offLast < offCur)
                                    cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);
                                switch (pszStr[offCur])
                                {
                                    case '<':   cchOutput += pfnOutput(pvArgOutput, "&lt;", 4); break;
                                    case '>':   cchOutput += pfnOutput(pvArgOutput, "&gt;", 4); break;
                                    case '&':   cchOutput += pfnOutput(pvArgOutput, "&amp;", 5); break;
                                    case '\'':  cchOutput += pfnOutput(pvArgOutput, "&apos;", 6); break;
                                    case '"':   cchOutput += pfnOutput(pvArgOutput, "&quot;", 6); break;
                                    case '\n':  cchOutput += pfnOutput(pvArgOutput, "&#xA;", 5); break;
                                    case '\r':  cchOutput += pfnOutput(pvArgOutput, "&#xD;", 5); break;
                                    default:
                                        AssertFailed();
                                }
                                offLast = offCur + 1;
                            }
                            offCur++;
                        }
                        if (offLast < offCur)
                            cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);

                        while (--cchWidth >= cchStr)
                            cchOutput += pfnOutput(pvArgOutput, " ", 1);
                        if (fAttr)
                            cchOutput += pfnOutput(pvArgOutput, "\"", 1);
                        return cchOutput;
                    }

                    default:
                        AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));
                }
                break;
            }
#endif /* IN_RING3 */


            /*
             * Groups 6 - CPU Architecture Register Formatters.
             *            "%RAarch[reg]"
             */
            case 'A':
            {
                char const * const  pszArch   = *ppszFormat;
                const char         *pszReg    = pszArch;
                size_t              cchOutput = 0;
                int                 cPrinted  = 0;
                size_t              cchReg;

                /* Parse out the */
                while ((ch = *pszReg++) && ch != '[')
                {   /* nothing */   }
                AssertMsgBreak(ch == '[', ("Malformed IPRT architecture register format type '%.10s'!\n", pszFormatOrg));

                cchReg = 0;
                while ((ch = pszReg[cchReg]) && ch != ']')
                    cchReg++;
                AssertMsgBreak(ch == ']', ("Malformed IPRT architecture register format type '%.10s'!\n", pszFormatOrg));

                *ppszFormat = &pszReg[cchReg + 1];


#define REG_EQUALS(a_szReg)  (sizeof(a_szReg) - 1 == cchReg && !strncmp(a_szReg, pszReg, sizeof(a_szReg) - 1))
#define REG_OUT_BIT(a_uVal, a_fBitMask, a_szName) \
    do { \
        if ((a_uVal) & (a_fBitMask)) \
        { \
            if (!cPrinted++) \
                cchOutput += pfnOutput(pvArgOutput, "{" a_szName, sizeof(a_szName)); \
            else \
                cchOutput += pfnOutput(pvArgOutput, "," a_szName, sizeof(a_szName)); \
           (a_uVal) &= ~(a_fBitMask); \
        } \
    } while (0)
#define REG_OUT_CLOSE(a_uVal) \
    do { \
        if ((a_uVal)) \
        { \
            cchOutput += pfnOutput(pvArgOutput, !cPrinted ? "{unkn=" : ",unkn=", 6); \
            cch = RTStrFormatNumber(&szBuf[0], (a_uVal), 16, 1, -1, fFlags); \
            cchOutput += pfnOutput(pvArgOutput, szBuf, cch); \
            cPrinted++; \
        } \
        if (cPrinted) \
            cchOutput += pfnOutput(pvArgOutput, "}", 1); \
    } while (0)


                if (0)
                { /* dummy */ }
#ifdef STRFORMAT_WITH_X86
                /*
                 * X86 & AMD64.
                 */
                else if (   pszReg - pszArch == 3 + 1
                         && pszArch[0] == 'x'
                         && pszArch[1] == '8'
                         && pszArch[2] == '6')
                {
                    if (REG_EQUALS("cr0"))
                    {
                        uint64_t cr0 = va_arg(*pArgs, uint64_t);
                        fFlags |= RTSTR_F_64BIT;
                        cch = RTStrFormatNumber(&szBuf[0], cr0, 16, 8, -1, fFlags | RTSTR_F_ZEROPAD);
                        cchOutput += pfnOutput(pvArgOutput, szBuf, cch);
                        REG_OUT_BIT(cr0, X86_CR0_PE, "PE");
                        REG_OUT_BIT(cr0, X86_CR0_MP, "MP");
                        REG_OUT_BIT(cr0, X86_CR0_EM, "EM");
                        REG_OUT_BIT(cr0, X86_CR0_TS, "DE");
                        REG_OUT_BIT(cr0, X86_CR0_ET, "ET");
                        REG_OUT_BIT(cr0, X86_CR0_NE, "NE");
                        REG_OUT_BIT(cr0, X86_CR0_WP, "WP");
                        REG_OUT_BIT(cr0, X86_CR0_AM, "AM");
                        REG_OUT_BIT(cr0, X86_CR0_NW, "NW");
                        REG_OUT_BIT(cr0, X86_CR0_CD, "CD");
                        REG_OUT_BIT(cr0, X86_CR0_PG, "PG");
                        REG_OUT_CLOSE(cr0);
                    }
                    else if (REG_EQUALS("cr4"))
                    {
                        uint64_t cr4 = va_arg(*pArgs, uint64_t);
                        fFlags |= RTSTR_F_64BIT;
                        cch = RTStrFormatNumber(&szBuf[0], cr4, 16, 8, -1, fFlags | RTSTR_F_ZEROPAD);
                        cchOutput += pfnOutput(pvArgOutput, szBuf, cch);
                        REG_OUT_BIT(cr4, X86_CR4_VME, "VME");
                        REG_OUT_BIT(cr4, X86_CR4_PVI, "PVI");
                        REG_OUT_BIT(cr4, X86_CR4_TSD, "TSD");
                        REG_OUT_BIT(cr4, X86_CR4_DE,  "DE");
                        REG_OUT_BIT(cr4, X86_CR4_PSE, "PSE");
                        REG_OUT_BIT(cr4, X86_CR4_PAE, "PAE");
                        REG_OUT_BIT(cr4, X86_CR4_MCE, "MCE");
                        REG_OUT_BIT(cr4, X86_CR4_PGE, "PGE");
                        REG_OUT_BIT(cr4, X86_CR4_PCE, "PCE");
                        REG_OUT_BIT(cr4, X86_CR4_OSFXSR, "OSFXSR");
                        REG_OUT_BIT(cr4, X86_CR4_OSXMMEEXCPT, "OSXMMEEXCPT");
                        REG_OUT_BIT(cr4, X86_CR4_VMXE, "VMXE");
                        REG_OUT_BIT(cr4, X86_CR4_SMXE, "SMXE");
                        REG_OUT_BIT(cr4, X86_CR4_PCIDE, "PCIDE");
                        REG_OUT_BIT(cr4, X86_CR4_OSXSAVE, "OSXSAVE");
                        REG_OUT_BIT(cr4, X86_CR4_SMEP, "SMEP");
                        REG_OUT_BIT(cr4, X86_CR4_SMAP, "SMAP");
                        REG_OUT_CLOSE(cr4);
                    }
                    else
                        AssertMsgFailed(("Unknown x86 register specified in '%.10s'!\n", pszFormatOrg));
                }
#endif
                else
                    AssertMsgFailed(("Unknown architecture specified in '%.10s'!\n", pszFormatOrg));
#undef REG_OUT_BIT
#undef REG_OUT_CLOSE
#undef REG_EQUALS
                return cchOutput;
            }

            /*
             * Invalid/Unknown. Bitch about it.
             */
            default:
                AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));
                break;
        }
    }
    else
        AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));

    NOREF(pszFormatOrg);
    return 0;
}

