summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/string/strformatrt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/string/strformatrt.cpp')
-rw-r--r--src/VBox/Runtime/common/string/strformatrt.cpp1722
1 files changed, 1722 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/string/strformatrt.cpp b/src/VBox/Runtime/common/string/strformatrt.cpp
new file mode 100644
index 00000000..eaedae3f
--- /dev/null
+++ b/src/VBox/Runtime/common/string/strformatrt.cpp
@@ -0,0 +1,1722 @@
+/* $Id: strformatrt.cpp $ */
+/** @file
+ * IPRT - IPRT String Formatter Extensions.
+ */
+
+/*
+ * 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>
+#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' && ch != '(')
+ {
+ 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;
+}
+