diff options
Diffstat (limited to 'src/VBox/Runtime/common/string/strformat.cpp')
-rw-r--r-- | src/VBox/Runtime/common/string/strformat.cpp | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/string/strformat.cpp b/src/VBox/Runtime/common/string/strformat.cpp new file mode 100644 index 00000000..86f23609 --- /dev/null +++ b/src/VBox/Runtime/common/string/strformat.cpp @@ -0,0 +1,866 @@ +/* $Id: strformat.cpp $ */ +/** @file + * IPRT - String Formatter. + */ + +/* + * Copyright (C) 2006-2023 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> +#include "internal/iprt.h" + +#include <iprt/assert.h> +#ifdef IN_RING3 +# include <iprt/alloc.h> +# include <iprt/errcore.h> +# include <iprt/uni.h> +# include <iprt/utf16.h> +#endif +#include <iprt/ctype.h> +#include <iprt/string.h> +#include <iprt/stdarg.h> +#include "internal/string.h" + + +/** + * Deals with bad pointers. + */ +DECLHIDDEN(size_t) rtStrFormatBadPointer(size_t cch, PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, int cchWidth, + unsigned fFlags, void const *pvStr, char szTmp[64], const char *pszTag, int cchTag) +{ + static char const s_szNull[] = "<NULL>"; + int cchStr = !pvStr ? sizeof(s_szNull) - 1 : 1 + sizeof(void *) * 2 + cchTag + 1; + + if (!(fFlags & RTSTR_F_LEFT)) + while (--cchWidth >= cchStr) + cch += pfnOutput(pvArgOutput, " ", 1); + + cchWidth -= cchStr; + if (!pvStr) + cch += pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1); + else + { + cch += pfnOutput(pvArgOutput, "<", 1); + cchStr = RTStrFormatNumber(&szTmp[0], (uintptr_t)pvStr, 16, sizeof(char *) * 2, 0, RTSTR_F_ZEROPAD); + cch += pfnOutput(pvArgOutput, szTmp, cchStr); + cch += pfnOutput(pvArgOutput, pszTag, cchTag); + cch += pfnOutput(pvArgOutput, ">", 1); + } + + while (--cchWidth >= 0) + cch += pfnOutput(pvArgOutput, " ", 1); + return cch; +} + + +/** + * Finds the length of a string up to cchMax. + * @returns Length. + * @param psz Pointer to string. + * @param cchMax Max length. + */ +static unsigned rtStrFormatStrNLen(const char *psz, unsigned cchMax) +{ + const char *pszC = psz; + + while (cchMax-- > 0 && *psz != '\0') + psz++; + + return (unsigned)(psz - pszC); +} + + +/** + * Finds the length of a string up to cchMax. + * @returns Length. + * @param pwsz Pointer to string. + * @param cchMax Max length. + */ +static unsigned rtStrFormatStrNLenUtf16(PCRTUTF16 pwsz, unsigned cchMax) +{ +#ifdef IN_RING3 + unsigned cwc = 0; + while (cchMax-- > 0) + { + RTUNICP cp; + int rc = RTUtf16GetCpEx(&pwsz, &cp); + AssertRC(rc); + if (RT_FAILURE(rc) || !cp) + break; + cwc++; + } + return cwc; +#else /* !IN_RING3 */ + PCRTUTF16 pwszC = pwsz; + + while (cchMax-- > 0 && *pwsz != '\0') + pwsz++; + + return (unsigned)(pwsz - pwszC); +#endif /* !IN_RING3 */ +} + + +/** + * Finds the length of a string up to cchMax. + * @returns Length. + * @param pusz Pointer to string. + * @param cchMax Max length. + */ +static unsigned rtStrFormatStrNLenUni(PCRTUNICP pusz, unsigned cchMax) +{ + PCRTUNICP puszC = pusz; + + while (cchMax-- > 0 && *pusz != '\0') + pusz++; + + return (unsigned)(pusz - puszC); +} + + +RTDECL(int) RTStrFormatNumber(char *psz, uint64_t u64Value, unsigned int uiBase, signed int cchWidth, signed int cchPrecision, + unsigned int fFlags) +{ + const char *pachDigits = "0123456789abcdef"; + char *pszStart = psz; + int cchMax; + int cchValue; + int i; + int j; + char chSign; + + /* + * Validate and adjust input... + */ + Assert(uiBase >= 2 && uiBase <= 16); + if (fFlags & RTSTR_F_CAPITAL) + pachDigits = "0123456789ABCDEF"; + if (fFlags & RTSTR_F_LEFT) + fFlags &= ~RTSTR_F_ZEROPAD; + if ( (fFlags & RTSTR_F_THOUSAND_SEP) + && ( uiBase != 10 + || (fFlags & RTSTR_F_ZEROPAD))) /** @todo implement RTSTR_F_ZEROPAD + RTSTR_F_THOUSAND_SEP. */ + fFlags &= ~RTSTR_F_THOUSAND_SEP; + + /* + * Determine value length and sign. Converts the u64Value to unsigned. + */ + cchValue = 0; + chSign = '\0'; + if ((fFlags & RTSTR_F_64BIT) || (u64Value & UINT64_C(0xffffffff00000000))) + { + uint64_t u64; + if (!(fFlags & RTSTR_F_VALSIGNED) || !(u64Value & RT_BIT_64(63))) + u64 = u64Value; + else if (u64Value != RT_BIT_64(63)) + { + chSign = '-'; + u64 = u64Value = -(int64_t)u64Value; + } + else + { + chSign = '-'; + u64 = u64Value = RT_BIT_64(63); + } + do + { + cchValue++; + u64 /= uiBase; + } while (u64); + } + else + { + uint32_t u32 = (uint32_t)u64Value; + if (!(fFlags & RTSTR_F_VALSIGNED) || !(u32 & UINT32_C(0x80000000))) + { /* likley */ } + else if (u32 != UINT32_C(0x80000000)) + { + chSign = '-'; + u64Value = u32 = -(int32_t)u32; + } + else + { + chSign = '-'; + u64Value = u32 = UINT32_C(0x80000000); + } + do + { + cchValue++; + u32 /= uiBase; + } while (u32); + } + if (fFlags & RTSTR_F_THOUSAND_SEP) + { + if (cchValue <= 3) + fFlags &= ~RTSTR_F_THOUSAND_SEP; + else + cchValue += cchValue / 3 - (cchValue % 3 == 0); + } + + /* + * Sign (+/-). + */ + i = 0; + if (fFlags & RTSTR_F_VALSIGNED) + { + if (chSign != '\0') + psz[i++] = chSign; + else if (fFlags & (RTSTR_F_PLUS | RTSTR_F_BLANK)) + psz[i++] = (char)(fFlags & RTSTR_F_PLUS ? '+' : ' '); + } + + /* + * Special (0/0x). + */ + if ((fFlags & RTSTR_F_SPECIAL) && (uiBase % 8) == 0) + { + psz[i++] = '0'; + if (uiBase == 16) + psz[i++] = (char)(fFlags & RTSTR_F_CAPITAL ? 'X' : 'x'); + } + + /* + * width - only if ZEROPAD + */ + cchMax = 64 - (cchValue + i + 1); /* HACK! 64 bytes seems to be the usual buffer size... */ + cchWidth -= i + cchValue; + if (fFlags & RTSTR_F_ZEROPAD) + while (--cchWidth >= 0 && i < cchMax) + { + AssertBreak(i < cchMax); + psz[i++] = '0'; + cchPrecision--; + } + else if (!(fFlags & RTSTR_F_LEFT) && cchWidth > 0) + { + AssertStmt(cchWidth < cchMax, cchWidth = cchMax - 1); + for (j = i - 1; j >= 0; j--) + psz[cchWidth + j] = psz[j]; + for (j = 0; j < cchWidth; j++) + psz[j] = ' '; + i += cchWidth; + } + + /* + * precision + */ + while (--cchPrecision >= cchValue) + { + AssertBreak(i < cchMax); + psz[i++] = '0'; + } + + psz += i; + + /* + * write number - not good enough but it works + */ + psz += cchValue; + i = -1; + if ((fFlags & RTSTR_F_64BIT) || (u64Value & UINT64_C(0xffffffff00000000))) + { + uint64_t u64 = u64Value; + if (fFlags & RTSTR_F_THOUSAND_SEP) + { + do + { + if ((-i - 1) % 4 == 3) + psz[i--] = ' '; + psz[i--] = pachDigits[u64 % uiBase]; + u64 /= uiBase; + } while (u64); + } + else + { + do + { + psz[i--] = pachDigits[u64 % uiBase]; + u64 /= uiBase; + } while (u64); + } + } + else + { + uint32_t u32 = (uint32_t)u64Value; + if (fFlags & RTSTR_F_THOUSAND_SEP) + { + do + { + if ((-i - 1) % 4 == 3) + psz[i--] = ' '; + psz[i--] = pachDigits[u32 % uiBase]; + u32 /= uiBase; + } while (u32); + } + else + { + do + { + psz[i--] = pachDigits[u32 % uiBase]; + u32 /= uiBase; + } while (u32); + } + } + + /* + * width if RTSTR_F_LEFT + */ + if (fFlags & RTSTR_F_LEFT) + while (--cchWidth >= 0) + *psz++ = ' '; + + *psz = '\0'; + return (unsigned)(psz - pszStart); +} +RT_EXPORT_SYMBOL(RTStrFormatNumber); + + +RTDECL(size_t) RTStrFormatV(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, + const char *pszFormat, va_list InArgs) +{ + char szTmp[64]; /* Worker functions assumes 64 byte buffer! Ugly but faster. */ + va_list args; + size_t cch = 0; + const char *pszStartOutput = pszFormat; + + va_copy(args, InArgs); /* make a copy so we can reference it (AMD64 / gcc). */ + + while (*pszFormat != '\0') + { + if (*pszFormat == '%') + { + /* output pending string. */ + if (pszStartOutput != pszFormat) + cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput); + + /* skip '%' */ + pszFormat++; + if (*pszFormat == '%') /* '%%'-> '%' */ + pszStartOutput = pszFormat++; + else + { + unsigned int fFlags = 0; + int cchWidth = -1; + int cchPrecision = -1; + unsigned int uBase = 10; + char chArgSize; + + /* flags */ + for (;;) + { + switch (*pszFormat++) + { + case '#': fFlags |= RTSTR_F_SPECIAL; continue; + case '-': fFlags |= RTSTR_F_LEFT; continue; + case '+': fFlags |= RTSTR_F_PLUS; continue; + case ' ': fFlags |= RTSTR_F_BLANK; continue; + case '0': fFlags |= RTSTR_F_ZEROPAD; continue; + case '\'': fFlags |= RTSTR_F_THOUSAND_SEP; continue; + } + pszFormat--; + break; + } + + /* width */ + if (RT_C_IS_DIGIT(*pszFormat)) + { + for (cchWidth = 0; RT_C_IS_DIGIT(*pszFormat); pszFormat++) + { + cchWidth *= 10; + cchWidth += *pszFormat - '0'; + } + fFlags |= RTSTR_F_WIDTH; + } + else if (*pszFormat == '*') + { + pszFormat++; + cchWidth = va_arg(args, int); + if (cchWidth < 0) + { + cchWidth = -cchWidth; + fFlags |= RTSTR_F_LEFT; + } + fFlags |= RTSTR_F_WIDTH; + } + + /* precision */ + if (*pszFormat == '.') + { + pszFormat++; + if (RT_C_IS_DIGIT(*pszFormat)) + { + for (cchPrecision = 0; RT_C_IS_DIGIT(*pszFormat); pszFormat++) + { + cchPrecision *= 10; + cchPrecision += *pszFormat - '0'; + } + + } + else if (*pszFormat == '*') + { + pszFormat++; + cchPrecision = va_arg(args, int); + } + if (cchPrecision < 0) + cchPrecision = 0; + fFlags |= RTSTR_F_PRECISION; + } + + /* + * Argument size. + */ + chArgSize = *pszFormat; + switch (chArgSize) + { + default: + chArgSize = 0; + break; + + case 'z': + case 'L': + case 'j': + case 't': + pszFormat++; + break; + + case 'l': + pszFormat++; + if (*pszFormat == 'l') + { + chArgSize = 'L'; + pszFormat++; + } + break; + + case 'h': + pszFormat++; + if (*pszFormat == 'h') + { + chArgSize = 'H'; + pszFormat++; + } + break; + + case 'I': /* Used by Win32/64 compilers. */ + if ( pszFormat[1] == '6' + && pszFormat[2] == '4') + { + pszFormat += 3; + chArgSize = 'L'; + } + else if ( pszFormat[1] == '3' + && pszFormat[2] == '2') + { + pszFormat += 3; + chArgSize = 0; + } + else + { + pszFormat += 1; + chArgSize = 'j'; + } + break; + + case 'q': /* Used on BSD platforms. */ + pszFormat++; + chArgSize = 'L'; + break; + } + + /* + * The type. + */ + switch (*pszFormat++) + { + /* char */ + case 'c': + { + if (!(fFlags & RTSTR_F_LEFT)) + while (--cchWidth > 0) + cch += pfnOutput(pvArgOutput, " ", 1); + + szTmp[0] = (char)va_arg(args, int); + szTmp[1] = '\0'; /* Some output functions wants terminated strings. */ + cch += pfnOutput(pvArgOutput, &szTmp[0], 1); + + while (--cchWidth > 0) + cch += pfnOutput(pvArgOutput, " ", 1); + break; + } + + case 'S': /* Legacy, conversion done by streams now. */ + case 's': + { + if (chArgSize == 'l') + { + /* utf-16 -> utf-8 */ + PCRTUTF16 pwszStr = va_arg(args, PCRTUTF16); + if (RT_VALID_PTR(pwszStr)) + { + int cwcStr = rtStrFormatStrNLenUtf16(pwszStr, (unsigned)cchPrecision); + if (!(fFlags & RTSTR_F_LEFT)) + while (--cchWidth >= cwcStr) + cch += pfnOutput(pvArgOutput, " ", 1); + cchWidth -= cwcStr; + while (cwcStr-- > 0) + { +/** @todo \#ifndef IN_RC*/ +#ifdef IN_RING3 + RTUNICP Cp; + RTUtf16GetCpEx(&pwszStr, &Cp); + char *pszEnd = RTStrPutCp(szTmp, Cp); + *pszEnd = '\0'; + cch += pfnOutput(pvArgOutput, szTmp, pszEnd - szTmp); +#else + char ch = (char)*pwszStr++; + cch += pfnOutput(pvArgOutput, &ch, 1); +#endif + } + while (--cchWidth >= 0) + cch += pfnOutput(pvArgOutput, " ", 1); + } + else + cch = rtStrFormatBadPointer(cch, pfnOutput, pvArgOutput, cchWidth, fFlags, + pwszStr, szTmp, RT_STR_TUPLE("!BadStrW")); + } + else if (chArgSize == 'L') + { + /* unicp -> utf8 */ + PCRTUNICP puszStr = va_arg(args, PCRTUNICP); + if (RT_VALID_PTR(puszStr)) + { + int cchStr = rtStrFormatStrNLenUni(puszStr, (unsigned)cchPrecision); + if (!(fFlags & RTSTR_F_LEFT)) + while (--cchWidth >= cchStr) + cch += pfnOutput(pvArgOutput, " ", 1); + + cchWidth -= cchStr; + while (cchStr-- > 0) + { +/** @todo \#ifndef IN_RC*/ +#ifdef IN_RING3 + char *pszEnd = RTStrPutCp(szTmp, *puszStr++); + cch += pfnOutput(pvArgOutput, szTmp, pszEnd - szTmp); +#else + char ch = (char)*puszStr++; + cch += pfnOutput(pvArgOutput, &ch, 1); +#endif + } + while (--cchWidth >= 0) + cch += pfnOutput(pvArgOutput, " ", 1); + } + else + cch = rtStrFormatBadPointer(cch, pfnOutput, pvArgOutput, cchWidth, fFlags, + puszStr, szTmp, RT_STR_TUPLE("!BadStrU")); + } + else + { + const char *pszStr = va_arg(args, const char *); + if (RT_VALID_PTR(pszStr)) + { + int cchStr = rtStrFormatStrNLen(pszStr, (unsigned)cchPrecision); + if (!(fFlags & RTSTR_F_LEFT)) + while (--cchWidth >= cchStr) + cch += pfnOutput(pvArgOutput, " ", 1); + + cch += pfnOutput(pvArgOutput, pszStr, cchStr); + + while (--cchWidth >= cchStr) + cch += pfnOutput(pvArgOutput, " ", 1); + } + else + cch = rtStrFormatBadPointer(cch, pfnOutput, pvArgOutput, cchWidth, fFlags, + pszStr, szTmp, RT_STR_TUPLE("!BadStr")); + } + break; + } + + /*-----------------*/ + /* integer/pointer */ + /*-----------------*/ + case 'd': + case 'i': + case 'o': + case 'p': + case 'u': + case 'x': + case 'X': + { + int cchNum; + uint64_t u64Value; + + switch (pszFormat[-1]) + { + case 'd': /* signed decimal integer */ + case 'i': + fFlags |= RTSTR_F_VALSIGNED; + break; + + case 'o': + uBase = 8; + break; + + case 'p': + fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */ + uBase = 16; + if (cchWidth < 0) + cchWidth = sizeof(char *) * 2; + break; + + case 'u': + uBase = 10; + break; + + case 'X': + fFlags |= RTSTR_F_CAPITAL; + RT_FALL_THRU(); + case 'x': + uBase = 16; + break; + } + + if (pszFormat[-1] == 'p') + u64Value = va_arg(args, uintptr_t); + else if (fFlags & RTSTR_F_VALSIGNED) + { + if (chArgSize == 'L') + { + u64Value = va_arg(args, int64_t); + fFlags |= RTSTR_F_64BIT; + } + else if (chArgSize == 'l') + { + u64Value = va_arg(args, signed long); + fFlags |= RTSTR_GET_BIT_FLAG(unsigned long); + } + else if (chArgSize == 'h') + { + u64Value = va_arg(args, /* signed short */ int); + fFlags |= RTSTR_GET_BIT_FLAG(signed short); + } + else if (chArgSize == 'H') + { + u64Value = va_arg(args, /* int8_t */ int); + fFlags |= RTSTR_GET_BIT_FLAG(int8_t); + } + else if (chArgSize == 'j') + { + u64Value = va_arg(args, /*intmax_t*/ int64_t); + fFlags |= RTSTR_F_64BIT; + } + else if (chArgSize == 'z') + { + u64Value = va_arg(args, size_t); + fFlags |= RTSTR_GET_BIT_FLAG(size_t); + } + else if (chArgSize == 't') + { + u64Value = va_arg(args, ptrdiff_t); + fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t); + } + else + { + u64Value = va_arg(args, signed int); + fFlags |= RTSTR_GET_BIT_FLAG(signed int); + } + } + else + { + if (chArgSize == 'L') + { + u64Value = va_arg(args, uint64_t); + fFlags |= RTSTR_F_64BIT; + } + else if (chArgSize == 'l') + { + u64Value = va_arg(args, unsigned long); + fFlags |= RTSTR_GET_BIT_FLAG(unsigned long); + } + else if (chArgSize == 'h') + { + u64Value = va_arg(args, /* unsigned short */ int); + fFlags |= RTSTR_GET_BIT_FLAG(unsigned short); + } + else if (chArgSize == 'H') + { + u64Value = va_arg(args, /* uint8_t */ int); + fFlags |= RTSTR_GET_BIT_FLAG(uint8_t); + } + else if (chArgSize == 'j') + { + u64Value = va_arg(args, /*uintmax_t*/ int64_t); + fFlags |= RTSTR_F_64BIT; + } + else if (chArgSize == 'z') + { + u64Value = va_arg(args, size_t); + fFlags |= RTSTR_GET_BIT_FLAG(size_t); + } + else if (chArgSize == 't') + { + u64Value = va_arg(args, ptrdiff_t); + fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t); + } + else + { + u64Value = va_arg(args, unsigned int); + fFlags |= RTSTR_GET_BIT_FLAG(unsigned int); + } + } + cchNum = RTStrFormatNumber(&szTmp[0], u64Value, uBase, cchWidth, cchPrecision, fFlags); + cch += pfnOutput(pvArgOutput, &szTmp[0], cchNum); + break; + } + + /* + * Floating point. + * + * We currently don't really implement these yet, there is just a very basic + * formatting regardless of the requested type. + */ + case 'e': /* [-]d.dddddde+-dd[d] */ + case 'E': /* [-]d.ddddddE+-dd[d] */ + case 'f': /* [-]dddd.dddddd / inf / nan */ + case 'F': /* [-]dddd.dddddd / INF / NAN */ + case 'g': /* Either f or e, depending on the magnitue and precision. */ + case 'G': /* Either f or E, depending on the magnitue and precision. */ + case 'a': /* [-]0xh.hhhhhhp+-dd */ + case 'A': /* [-]0Xh.hhhhhhP+-dd */ + { +#if defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3) && !defined(IPRT_NO_FLOAT_FORMATTING) + size_t cchNum; +# ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE + if (chArgSize == 'L') + { + RTFLOAT80U2 r80; + r80.lrd = va_arg(args, long double); +# ifndef IN_BLD_PROG + cchNum = RTStrFormatR80u2(&szTmp[0], sizeof(szTmp), &r80, cchWidth, cchPrecision, 0); +# else + cch += pfnOutput(pvArgOutput, RT_STR_TUPLE("<long double>")); + RT_NOREF_PV(r80); + break; +# endif + } + else +# endif + { + RTFLOAT64U r64; + r64.rd = va_arg(args, double); +# ifndef IN_BLD_PROG + cchNum = RTStrFormatR64(&szTmp[0], sizeof(szTmp), &r64, cchWidth, cchPrecision, 0); +# else + cchNum = RTStrFormatNumber(&szTmp[0], r64.au64[0], 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_64BIT); +# endif + } + cch += pfnOutput(pvArgOutput, &szTmp[0], cchNum); +#else /* !IN_RING3 */ + AssertFailed(); +#endif /* !IN_RING3 */ + break; + } + + /* + * Nested extensions. + */ + case 'M': /* replace the format string (not stacked yet). */ + { + pszStartOutput = pszFormat = va_arg(args, const char *); + AssertPtr(pszStartOutput); + break; + } + + case 'N': /* real nesting. */ + { + const char *pszFormatNested = va_arg(args, const char *); + va_list *pArgsNested = va_arg(args, va_list *); + va_list ArgsNested; + va_copy(ArgsNested, *pArgsNested); + Assert(pszFormatNested); + cch += RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormatNested, ArgsNested); + va_end(ArgsNested); + break; + } + + /* + * IPRT Extensions. + */ + case 'R': + { + if (*pszFormat != '[') + { + pszFormat--; + cch += rtstrFormatRt(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize); + } + else + { + pszFormat--; + cch += rtstrFormatType(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize); + } + break; + } + + /* + * Custom format. + */ + default: + { + if (pfnFormat) + { + pszFormat--; + cch += pfnFormat(pvArgFormat, pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize); + } + break; + } + } + pszStartOutput = pszFormat; + } + } + else + pszFormat++; + } + + /* output pending string. */ + if (pszStartOutput != pszFormat) + cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput); + + /* terminate the output */ + pfnOutput(pvArgOutput, NULL, 0); + + return cch; +} +RT_EXPORT_SYMBOL(RTStrFormatV); + |