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

/*
 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
 *
 * This file is part of VirtualBox base platform packages, as
 * available from https://www.virtualbox.org.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, in version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses>.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
 * in the VirtualBox 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.
 *
 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
 */


/*********************************************************************************************************************************
*   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/errcore.h>
# include <iprt/thread.h>
# include <iprt/utf16.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";
#ifdef IN_RING3
static char g_szHexDigitsUpper[17] = "0123456789ABCDEF";
#endif


/**
 * 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]; RT_FALL_THRU();
        case 3: pszDst[off++] = g_szHexDigits[(uWord >>  8) & 0xf]; RT_FALL_THRU();
        case 2: pszDst[off++] = g_szHexDigits[(uWord >>  4) & 0xf]; RT_FALL_THRU();
        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':
            case 'K':
            {
                /*
                 * 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_ERRINFO,
                    RTSF_ERRINFO_MSG_ONLY
                } 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("Kv"),      sizeof(RTHCPTR),        16, RTSF_INT,   RTSTR_F_OBFUSCATE_PTR },
                    { STRMEM("Rv"),      sizeof(RTRCPTR),        16, RTSF_INTW,  0 },
                    { STRMEM("Tbool"),   sizeof(bool),           10, RTSF_BOOL,  0 },
                    { STRMEM("Teic"),    sizeof(PCRTERRINFO),    16, RTSF_ERRINFO, 0 },
                    { STRMEM("Teim"),    sizeof(PCRTERRINFO),    16, RTSF_ERRINFO_MSG_ONLY, 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
                };

                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;
                    RTR0INTPTR          uR0Ptr;
                    RTFAR16             fp16;
                    RTFAR32             fp32;
                    RTFAR64             fp64;
                    bool                fBool;
                    PCRTMAC             pMac;
                    RTNETADDRIPV4       Ipv4Addr;
                    PCRTNETADDRIPV6     pIpv6Addr;
                    PCRTNETADDR         pNetAddr;
                    PCRTUUID            pUuid;
                    PCRTERRINFO         pErrInfo;
                } u;

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

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

#ifndef DEBUG
                /*
                 * For now don't show the address.
                 */
                if (fFlags & RTSTR_F_OBFUSCATE_PTR)
                {
                    cch = rtStrFormatKernelAddress(szBuf, sizeof(szBuf), u.uR0Ptr, cchWidth, cchPrecision, fFlags);
                    return pfnOutput(pvArgOutput, szBuf, cch);
                }
#endif

                /*
                 * 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) /* 2021-03-19: Only trailing space for %#RTbool. */
                            return pfnOutput(pvArgOutput, s_szTrue, sizeof(s_szTrue) - (fFlags & RTSTR_F_SPECIAL ? 1 : 2));
                        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 (RT_VALID_PTR(u.pIpv6Addr))
                            return rtstrFormatIPv6(pfnOutput, pvArgOutput, u.pIpv6Addr);
                        return rtStrFormatBadPointer(0, pfnOutput, pvArgOutput, cchWidth, fFlags, u.pIpv6Addr,
                                                     szBuf, RT_STR_TUPLE("!BadIPv6"));

                    case RTSF_MAC:
                        if (RT_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 rtStrFormatBadPointer(0, pfnOutput, pvArgOutput, cchWidth, fFlags, u.pMac,
                                                     szBuf, RT_STR_TUPLE("!BadMac"));

                    case RTSF_NETADDR:
                        if (RT_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 rtStrFormatBadPointer(0, pfnOutput, pvArgOutput, cchWidth, fFlags, u.pNetAddr,
                                                     szBuf, RT_STR_TUPLE("!BadNetAddr"));

                    case RTSF_UUID:
                        if (RT_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 rtStrFormatBadPointer(0, pfnOutput, pvArgOutput, cchWidth, fFlags, u.pUuid,
                                                     szBuf, RT_STR_TUPLE("!BadUuid"));

                    case RTSF_ERRINFO:
                    case RTSF_ERRINFO_MSG_ONLY:
                        if (RT_VALID_PTR(u.pErrInfo) && RTErrInfoIsSet(u.pErrInfo))
                        {
                            cch = 0;
                            if (s_aTypes[i].enmFormat == RTSF_ERRINFO)
                            {
#ifdef IN_RING3 /* we don't want this anywhere else yet. */
                                cch += RTErrFormatMsgShort(u.pErrInfo->rc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
#else
                                cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%d", u.pErrInfo->rc);
#endif
                            }

                            if (u.pErrInfo->cbMsg > 0)
                            {
                                if (fFlags & RTSTR_F_SPECIAL)
                                    cch = pfnOutput(pvArgOutput, RT_STR_TUPLE(" - "));
                                else
                                    cch = pfnOutput(pvArgOutput, RT_STR_TUPLE(": "));
                                cch += pfnOutput(pvArgOutput, u.pErrInfo->pszMsg,
                                                 RTStrNLen(u.pErrInfo->pszMsg, u.pErrInfo->cbMsg));
                            }
                            return cch;
                        }
                        return 0;

                    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, big endian UTF-16.
             */
            case 'b':
            {
                switch (*(*ppszFormat)++)
                {
                    case 'n':
                    {
                        const char *pszLastSep;
                        const char *psz = pszLastSep = va_arg(*pArgs, const char *);
                        if (!RT_VALID_PTR(psz))
                            return rtStrFormatBadPointer(0, pfnOutput, pvArgOutput, cchWidth, fFlags, psz,
                                                         szBuf, RT_STR_TUPLE("!BadBaseName"));

                        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);
                    }

                    /* %lRbs */
                    case 's':
                        if (chArgSize == 'l')
                        {
                            /* utf-16BE -> utf-8 */
                            int         cchStr;
                            PCRTUTF16   pwszStr = va_arg(*pArgs, PRTUTF16);

                            if (RT_VALID_PTR(pwszStr))
                            {
                                cchStr = 0;
                                while (cchStr < cchPrecision && pwszStr[cchStr] != '\0')
                                    cchStr++;
                            }
                            else
                            {
                                static RTUTF16  s_wszBigNull[] =
                                {
                                    RT_H2BE_U16_C((uint16_t)'<'), RT_H2BE_U16_C((uint16_t)'N'), RT_H2BE_U16_C((uint16_t)'U'),
                                    RT_H2BE_U16_C((uint16_t)'L'), RT_H2BE_U16_C((uint16_t)'L'), RT_H2BE_U16_C((uint16_t)'>'), '\0'
                                };
                                pwszStr = s_wszBigNull;
                                cchStr  = RT_ELEMENTS(s_wszBigNull) - 1;
                            }

                            cch = 0;
                            if (!(fFlags & RTSTR_F_LEFT))
                                while (--cchWidth >= cchStr)
                                    cch += pfnOutput(pvArgOutput, " ", 1);
                            cchWidth -= cchStr;
                            while (cchStr-- > 0)
                            {
/** @todo \#ifndef IN_RC*/
#ifdef IN_RING3
                                RTUNICP Cp = 0;
                                RTUtf16BigGetCpEx(&pwszStr, &Cp);
                                char *pszEnd = RTStrPutCp(szBuf, Cp);
                                *pszEnd = '\0';
                                cch += pfnOutput(pvArgOutput, szBuf, pszEnd - szBuf);
#else
                                szBuf[0] = (char)(*pwszStr++ >> 8);
                                cch += pfnOutput(pvArgOutput, szBuf, 1);
#endif
                            }
                            while (--cchWidth >= 0)
                                cch += pfnOutput(pvArgOutput, " ", 1);
                            return cch;
                        }
                    RT_FALL_THRU();

                    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 *);
                        int cAngle = 0;

                        if (!RT_VALID_PTR(psz))
                            return rtStrFormatBadPointer(0, pfnOutput, pvArgOutput, cchWidth, fFlags, psz,
                                                         szBuf, RT_STR_TUPLE("!BadFnNm"));

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

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

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


            /*
             * hex dumping, COM/XPCOM, human readable sizes.
             */
            case 'h':
            {
                ch = *(*ppszFormat)++;
                switch (ch)
                {
                    /*
                     * Hex stuff.
                     */
                    case 'x':
                    case 'X':
                    {
                        uint8_t *pu8 = va_arg(*pArgs, uint8_t *);
                        uint64_t uMemAddr;
                        int      cchMemAddrWidth;

                        if (cchPrecision < 0)
                            cchPrecision = 16;

                        if (ch == 'x')
                        {
                            uMemAddr = (uintptr_t)pu8;
                            cchMemAddrWidth = sizeof(pu8) * 2;
                        }
                        else
                        {
                            uMemAddr = va_arg(*pArgs, uint64_t);
                            cchMemAddrWidth = uMemAddr > UINT32_MAX || uMemAddr + cchPrecision > UINT32_MAX ? 16 : 8;
                        }

                        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*llx/%04x:",
                                                           off ? "\n" : "", cchMemAddrWidth, uMemAddr + off, 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;
                                }

                                /*
                                 * Regular hex dump with dittoing.
                                 */
                                case 'D':
                                {
                                    int offEndDupCheck;
                                    int cDuplicates = 0;
                                    int off = 0;
                                    cch = 0;

                                    if (cchWidth <= 0)
                                        cchWidth = 16;
                                    offEndDupCheck = cchPrecision - cchWidth;

                                    while (off < cchPrecision)
                                    {
                                        int i;
                                        if (   off >= offEndDupCheck
                                            || off <= 0
                                            || memcmp(pu8, pu8 - cchWidth, cchWidth) != 0
                                            || (   cDuplicates == 0
                                                && (   off + cchWidth >= offEndDupCheck
                                                    || memcmp(pu8 + cchWidth, pu8, cchWidth) != 0)) )
                                        {
                                            if (cDuplicates > 0)
                                            {
                                                cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "\n%.*s ****  <ditto x %u>",
                                                                   cchMemAddrWidth, "****************", cDuplicates);
                                                cDuplicates = 0;
                                            }

                                            cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s%0*llx/%04x:",
                                                               off ? "\n" : "", cchMemAddrWidth, uMemAddr + off, 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);
                                            }
                                        }
                                        else
                                            cDuplicates++;

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

                                /*
                                 * Hex string.
                                 * The default separator is ' ', RTSTR_F_THOUSAND_SEP changes it to ':',
                                 * and RTSTR_F_SPECIAL removes it.
                                 */
                                case 's':
                                {
                                    if (cchPrecision-- > 0)
                                    {
                                        if (ch == 'x')
                                            cch = RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%02x", *pu8++);
                                        else
                                            cch = RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%0*llx: %02x",
                                                              cchMemAddrWidth, uMemAddr, *pu8++);
                                        if (!(fFlags & (RTSTR_F_SPECIAL | RTSTR_F_THOUSAND_SEP)))
                                            for (; cchPrecision > 0; cchPrecision--, pu8++)
                                                cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, " %02x", *pu8);
                                        else if (fFlags & RTSTR_F_SPECIAL)
                                            for (; cchPrecision > 0; cchPrecision--, pu8++)
                                                cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%02x", *pu8);
                                        else
                                            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);
# ifndef RT_OS_WINDOWS
                        PCRTCOMERRMSG pMsg = RTErrCOMGet(hrc);
# endif
                        switch (*(*ppszFormat)++)
                        {
# ifdef RT_OS_WINDOWS
                            case 'c':
                                return RTErrWinFormatDefine(hrc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
                            case 'f':
                                return RTErrWinFormatMsg(hrc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
                            case 'a':
                                return RTErrWinFormatMsgAll(hrc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
# else  /* !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, hrc, pMsg->pszMsgFull);
# endif /* !RT_OS_WINDOWS */
                            default:
                                AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
                                return 0;
                        }
                        break;
                    }
#endif /* IN_RING3 */

                    /*
                     * Human readable sizes.
                     */
                    case 'c':
                    case 'u':
                    {
                        unsigned    i;
                        ssize_t     cchBuf;
                        uint64_t    uValue;
                        uint64_t    uFraction = 0;
                        const char *pszPrefix = NULL;
                        char        ch2 = *(*ppszFormat)++;
                        AssertMsgReturn(ch2 == 'b' || ch2 == 'B' || ch2 == 'i', ("invalid type '%.10s'!\n", pszFormatOrg), 0);
                        uValue = va_arg(*pArgs, uint64_t);

                        if (!(fFlags & RTSTR_F_PRECISION))
                            cchPrecision = 1; /** @todo default to flexible decimal point. */
                        else if (cchPrecision > 3)
                            cchPrecision = 3;
                        else if (cchPrecision < 0)
                            cchPrecision = 0;

                        if (ch2 == 'b' || ch2 == 'B')
                        {
                            static const struct
                            {
                                const char *pszPrefix;
                                uint8_t     cShift;
                                uint64_t    cbMin;
                                uint64_t    cbMinZeroPrecision;
                            } s_aUnits[] =
                            {
                                {  "Ei", 60, _1E,  _1E*2 },
                                {  "Pi", 50, _1P,  _1P*2 },
                                {  "Ti", 40, _1T,  _1T*2 },
                                {  "Gi", 30, _1G,  _1G64*2 },
                                {  "Mi", 20, _1M,  _1M*2 },
                                {  "Ki", 10, _1K,  _1K*2 },
                            };
                            for (i = 0; i < RT_ELEMENTS(s_aUnits); i++)
                                if (   uValue >= s_aUnits[i].cbMin
                                    && (cchPrecision > 0 || uValue >= s_aUnits[i].cbMinZeroPrecision))
                                {
                                    if (cchPrecision != 0)
                                    {
                                        uFraction   = uValue & (RT_BIT_64(s_aUnits[i].cShift) - 1);
                                        uFraction  *= cchPrecision == 1 ? 10 : cchPrecision == 2 ? 100 : 1000;
                                        uFraction >>= s_aUnits[i].cShift;
                                    }
                                    uValue  >>= s_aUnits[i].cShift;
                                    pszPrefix = s_aUnits[i].pszPrefix;
                                    break;
                                }
                        }
                        else
                        {
                            static const struct
                            {
                                const char *pszPrefix;
                                uint64_t    cbFactor;
                                uint64_t    cbMinZeroPrecision;
                            } s_aUnits[] =
                            {
                                {  "E", UINT64_C(1000000000000000000), UINT64_C(1010000000000000000),  },
                                {  "P", UINT64_C(1000000000000000),    UINT64_C(1010000000000000),     },
                                {  "T", UINT64_C(1000000000000),       UINT64_C(1010000000000),        },
                                {  "G", UINT64_C(1000000000),          UINT64_C(1010000000),           },
                                {  "M", UINT64_C(1000000),             UINT64_C(1010000),              },
                                {  "k", UINT64_C(1000),                UINT64_C(1010),                 },
                            };
                            for (i = 0; i < RT_ELEMENTS(s_aUnits); i++)
                                if (   uValue >= s_aUnits[i].cbFactor
                                    && (cchPrecision > 0 || uValue >= s_aUnits[i].cbMinZeroPrecision))
                                {
                                    if (cchPrecision == 0)
                                        uValue /= s_aUnits[i].cbFactor;
                                    else
                                    {
                                        uFraction   = uValue % s_aUnits[i].cbFactor;
                                        uValue      = uValue / s_aUnits[i].cbFactor;
                                        uFraction  *= cchPrecision == 1 ? 10 : cchPrecision == 2 ? 100 : 1000;
                                        uFraction  += s_aUnits[i].cbFactor >> 1;
                                        uFraction  /= s_aUnits[i].cbFactor;
                                    }
                                    pszPrefix = s_aUnits[i].pszPrefix;
                                    break;
                                }
                        }

                        cchBuf = RTStrFormatU64(szBuf, sizeof(szBuf), uValue, 10, 0, 0, 0);
                        if (pszPrefix)
                        {
                            if (cchPrecision)
                            {
                                szBuf[cchBuf++] = '.';
                                cchBuf += RTStrFormatU64(&szBuf[cchBuf], sizeof(szBuf) - cchBuf, uFraction, 10, cchPrecision, 0,
                                                         RTSTR_F_ZEROPAD | RTSTR_F_WIDTH);
                            }
                            if (fFlags & RTSTR_F_BLANK)
                                szBuf[cchBuf++] = ' ';
                            szBuf[cchBuf++] = *pszPrefix++;
                            if (*pszPrefix && ch2 != 'B')
                                szBuf[cchBuf++] = *pszPrefix;
                        }
                        else if (fFlags & RTSTR_F_BLANK)
                            szBuf[cchBuf++] = ' ';
                        if (ch == 'c')
                            szBuf[cchBuf++] = 'B';
                        szBuf[cchBuf] = '\0';

                        cch = 0;
                        if ((fFlags & RTSTR_F_WIDTH) && !(fFlags & RTSTR_F_LEFT))
                            while (cchBuf < cchWidth)
                            {
                                cch += pfnOutput(pvArgOutput, fFlags & RTSTR_F_ZEROPAD ? "0" : " ", 1);
                                cchWidth--;
                            }
                        cch += pfnOutput(pvArgOutput, szBuf, cchBuf);
                        return cch;
                    }

                    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. */
                switch (*(*ppszFormat)++)
                {
                    case 'c':
                        return RTErrFormatDefine(rc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
                    case 's':
                        return RTErrFormatMsgShort(rc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
                    case 'f':
                        return RTErrFormatMsgFull(rc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
                    case 'a':
                        return RTErrFormatMsgAll(rc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
                    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);
                switch (*(*ppszFormat)++)
                {
# if defined(RT_OS_WINDOWS)
                    case 'c':
                        return RTErrWinFormatDefine(rc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
                    case 'f':
                        return RTErrWinFormatMsg(rc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
                    case 'a':
                        return RTErrWinFormatMsgAll(rc, pfnOutput, pvArgOutput, szBuf, sizeof(szBuf));
# else  /* !RT_OS_WINDOWS */
                    case 'c':
                    case 'f':
                    case 'a':
                        return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "0x%08x", rc);
# endif /* !RT_OS_WINDOWS */
                    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) && !RT_VALID_PTR(u.pv))
                    return rtStrFormatBadPointer(0, pfnOutput, pvArgOutput, cchWidth, fFlags, u.pv,
                                                 szBuf, RT_STR_TUPLE("!BadRD"));

                /*
                 * 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, JSON and URI escapers.
             */
            case 'M':
            {
                char chWhat = (*ppszFormat)[0];
                if (chWhat == 'a' || chWhat == 'e')
                {
                    /* XML attributes and element values. */
                    bool fAttr  = chWhat == 'a';
                    char chType = (*ppszFormat)[1];
                    *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 (!RT_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));
                    }
                }
                else if (chWhat == 'j')
                {
                    /* JSON string escaping. */
                    char const chType = (*ppszFormat)[1];
                    *ppszFormat += 2;
                    switch (chType)
                    {
                        case 's':
                        {
                            const char *pszStr = va_arg(*pArgs, char *);
                            size_t      cchOutput;
                            ssize_t     cchStr;
                            ssize_t     offCur;
                            ssize_t     offLast;

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

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

                            offLast = offCur = 0;
                            while (offCur < cchStr)
                            {
                                unsigned int const uch = pszStr[offCur];
                                if (   uch >= 0x5d
                                    || (uch >= 0x20 && uch != 0x22 && uch != 0x5c))
                                    offCur++;
                                else
                                {
                                    if (offLast < offCur)
                                        cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);
                                    switch ((char)uch)
                                    {
                                        case '"':   cchOutput += pfnOutput(pvArgOutput, "\\\"", 2); break;
                                        case '\\':  cchOutput += pfnOutput(pvArgOutput, "\\\\", 2); break;
                                        case '/':   cchOutput += pfnOutput(pvArgOutput, "\\/", 2); break;
                                        case '\b':  cchOutput += pfnOutput(pvArgOutput, "\\b", 2); break;
                                        case '\f':  cchOutput += pfnOutput(pvArgOutput, "\\f", 2); break;
                                        case '\n':  cchOutput += pfnOutput(pvArgOutput, "\\n", 2); break;
                                        case '\t':  cchOutput += pfnOutput(pvArgOutput, "\\t", 2); break;
                                        default:
                                        {
                                            RTUNICP     uc     = 0xfffd; /* replacement character */
                                            const char *pszCur = &pszStr[offCur];
                                            int rc = RTStrGetCpEx(&pszCur, &uc);
                                            if (RT_SUCCESS(rc))
                                                offCur += pszCur - &pszStr[offCur] - 1;
                                            if (uc >= 0xfffe)
                                                uc = 0xfffd;             /* replacement character */
                                            szBuf[0] = '\\';
                                            szBuf[1] = 'u';
                                            szBuf[2] = g_szHexDigits[(uc >> 12) & 0xf];
                                            szBuf[3] = g_szHexDigits[(uc >>  8) & 0xf];
                                            szBuf[4] = g_szHexDigits[(uc >>  4) & 0xf];
                                            szBuf[5] = g_szHexDigits[ uc        & 0xf];
                                            szBuf[6] = '\0';
                                            cchOutput += pfnOutput(pvArgOutput, szBuf, 6);
                                            break;
                                        }
                                    }
                                    offLast = ++offCur;
                                }
                            }
                            if (offLast < offCur)
                                cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);

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

                        default:
                            AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));
                    }
                }
                else if (chWhat == 'p')
                {
                    /* Percent encoded string (RTC-3986). */
                    char const  chVariant = (*ppszFormat)[1];
                    char const  chAddSafe = chVariant == 'p' ? '/'
                                          : chVariant == 'q' ? '+' /* '+' in queries is problematic, so no escape. */
                                          :                    '~' /* whatever */;
                    size_t      cchOutput = 0;
                    const char *pszStr    = va_arg(*pArgs, char *);
                    ssize_t     cchStr;
                    ssize_t     offCur;
                    ssize_t     offLast;

                    *ppszFormat += 2;
                    AssertMsgBreak(chVariant == 'a' || chVariant == 'p' || chVariant == 'q' || chVariant == 'f',
                                   ("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));

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

                    if (!(fFlags & RTSTR_F_LEFT))
                        while (--cchWidth >= cchStr)
                            cchOutput += pfnOutput(pvArgOutput, "%20", 3);

                    offLast = offCur = 0;
                    while (offCur < cchStr)
                    {
                        ch = pszStr[offCur];
                        if (   RT_C_IS_ALPHA(ch)
                            || RT_C_IS_DIGIT(ch)
                            || ch == '-'
                            || ch == '.'
                            || ch == '_'
                            || ch == '~'
                            || ch == chAddSafe)
                            offCur++;
                        else
                        {
                            if (offLast < offCur)
                                cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);
                            if (ch != ' ' || chVariant != 'f')
                            {
                                szBuf[0] = '%';
                                szBuf[1] = g_szHexDigitsUpper[((uint8_t)ch >> 4) & 0xf];
                                szBuf[2] = g_szHexDigitsUpper[(uint8_t)ch & 0xf];
                                szBuf[3] = '\0';
                                cchOutput += pfnOutput(pvArgOutput, szBuf, 3);
                            }
                            else
                                cchOutput += pfnOutput(pvArgOutput, "+", 1);
                            offLast = ++offCur;
                        }
                    }
                    if (offLast < offCur)
                        cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);

                    while (--cchWidth >= cchStr)
                        cchOutput += pfnOutput(pvArgOutput, "%20", 3);
                }
                else
                    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;
}

