VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/string/strformatrt.cpp@ 73909

Last change on this file since 73909 was 73909, checked in by vboxsync, 7 years ago

iprt/string.h: Added percent encoded formatters: %RMpf, %RMpp, %RMpq. bugref:9167

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 73.4 KB
Line 
1/* $Id: strformatrt.cpp 73909 2018-08-27 11:22:26Z vboxsync $ */
2/** @file
3 * IPRT - IPRT String Formatter Extensions.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_STRING
32#include <iprt/string.h>
33#ifndef RT_NO_EXPORT_SYMBOL
34# define RT_NO_EXPORT_SYMBOL /* don't slurp <linux/module.h> which then again
35 slurps arch-specific headers defining symbols */
36#endif
37#include "internal/iprt.h"
38
39#include <iprt/log.h>
40#include <iprt/assert.h>
41#include <iprt/string.h>
42#include <iprt/stdarg.h>
43#ifdef IN_RING3
44# include <iprt/thread.h>
45# include <iprt/err.h>
46#endif
47#include <iprt/ctype.h>
48#include <iprt/time.h>
49#include <iprt/net.h>
50#include <iprt/path.h>
51#include <iprt/asm.h>
52#define STRFORMAT_WITH_X86
53#ifdef STRFORMAT_WITH_X86
54# include <iprt/x86.h>
55#endif
56#include "internal/string.h"
57
58
59/*********************************************************************************************************************************
60* Global Variables *
61*********************************************************************************************************************************/
62static char g_szHexDigits[17] = "0123456789abcdef";
63static char g_szHexDigitsUpper[17] = "0123456789ABCDEF";
64
65
66/**
67 * Helper that formats a 16-bit hex word in a IPv6 address.
68 *
69 * @returns Length in chars.
70 * @param pszDst The output buffer. Written from the start.
71 * @param uWord The word to format as hex.
72 */
73static size_t rtstrFormatIPv6HexWord(char *pszDst, uint16_t uWord)
74{
75 size_t off;
76 uint16_t cDigits;
77
78 if (uWord & UINT16_C(0xff00))
79 cDigits = uWord & UINT16_C(0xf000) ? 4 : 3;
80 else
81 cDigits = uWord & UINT16_C(0x00f0) ? 2 : 1;
82
83 off = 0;
84 switch (cDigits)
85 {
86 case 4: pszDst[off++] = g_szHexDigits[(uWord >> 12) & 0xf]; RT_FALL_THRU();
87 case 3: pszDst[off++] = g_szHexDigits[(uWord >> 8) & 0xf]; RT_FALL_THRU();
88 case 2: pszDst[off++] = g_szHexDigits[(uWord >> 4) & 0xf]; RT_FALL_THRU();
89 case 1: pszDst[off++] = g_szHexDigits[(uWord >> 0) & 0xf];
90 break;
91 }
92 pszDst[off] = '\0';
93 return off;
94}
95
96
97/**
98 * Helper function to format IPv6 address according to RFC 5952.
99 *
100 * @returns The number of bytes formatted.
101 * @param pfnOutput Pointer to output function.
102 * @param pvArgOutput Argument for the output function.
103 * @param pIpv6Addr IPv6 address
104 */
105static size_t rtstrFormatIPv6(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PCRTNETADDRIPV6 pIpv6Addr)
106{
107 size_t cch; /* result */
108 bool fEmbeddedIpv4;
109 size_t cwHexPart;
110 size_t cwLongestZeroRun;
111 size_t iLongestZeroStart;
112 size_t idx;
113 char szHexWord[8];
114
115 Assert(pIpv6Addr != NULL);
116
117 /*
118 * Check for embedded IPv4 address.
119 *
120 * IPv4-compatible - ::11.22.33.44 (obsolete)
121 * IPv4-mapped - ::ffff:11.22.33.44
122 * IPv4-translated - ::ffff:0:11.22.33.44 (RFC 2765)
123 */
124 fEmbeddedIpv4 = false;
125 cwHexPart = RT_ELEMENTS(pIpv6Addr->au16);
126 if ( pIpv6Addr->au64[0] == 0
127 && ( ( pIpv6Addr->au32[2] == 0
128 && pIpv6Addr->au32[3] != 0
129 && pIpv6Addr->au32[3] != RT_H2BE_U32_C(1) )
130 || pIpv6Addr->au32[2] == RT_H2BE_U32_C(0x0000ffff)
131 || pIpv6Addr->au32[2] == RT_H2BE_U32_C(0xffff0000) ) )
132 {
133 fEmbeddedIpv4 = true;
134 cwHexPart -= 2;
135 }
136
137 /*
138 * Find the longest sequences of two or more zero words.
139 */
140 cwLongestZeroRun = 0;
141 iLongestZeroStart = 0;
142 for (idx = 0; idx < cwHexPart; idx++)
143 if (pIpv6Addr->au16[idx] == 0)
144 {
145 size_t iZeroStart = idx;
146 size_t cwZeroRun;
147 do
148 idx++;
149 while (idx < cwHexPart && pIpv6Addr->au16[idx] == 0);
150 cwZeroRun = idx - iZeroStart;
151 if (cwZeroRun > 1 && cwZeroRun > cwLongestZeroRun)
152 {
153 cwLongestZeroRun = cwZeroRun;
154 iLongestZeroStart = iZeroStart;
155 if (cwZeroRun >= cwHexPart - idx)
156 break;
157 }
158 }
159
160 /*
161 * Do the formatting.
162 */
163 cch = 0;
164 if (cwLongestZeroRun == 0)
165 {
166 for (idx = 0; idx < cwHexPart; ++idx)
167 {
168 if (idx > 0)
169 cch += pfnOutput(pvArgOutput, ":", 1);
170 cch += pfnOutput(pvArgOutput, szHexWord, rtstrFormatIPv6HexWord(szHexWord, RT_BE2H_U16(pIpv6Addr->au16[idx])));
171 }
172
173 if (fEmbeddedIpv4)
174 cch += pfnOutput(pvArgOutput, ":", 1);
175 }
176 else
177 {
178 const size_t iLongestZeroEnd = iLongestZeroStart + cwLongestZeroRun;
179
180 if (iLongestZeroStart == 0)
181 cch += pfnOutput(pvArgOutput, ":", 1);
182 else
183 for (idx = 0; idx < iLongestZeroStart; ++idx)
184 {
185 cch += pfnOutput(pvArgOutput, szHexWord, rtstrFormatIPv6HexWord(szHexWord, RT_BE2H_U16(pIpv6Addr->au16[idx])));
186 cch += pfnOutput(pvArgOutput, ":", 1);
187 }
188
189 if (iLongestZeroEnd == cwHexPart)
190 cch += pfnOutput(pvArgOutput, ":", 1);
191 else
192 {
193 for (idx = iLongestZeroEnd; idx < cwHexPart; ++idx)
194 {
195 cch += pfnOutput(pvArgOutput, ":", 1);
196 cch += pfnOutput(pvArgOutput, szHexWord, rtstrFormatIPv6HexWord(szHexWord, RT_BE2H_U16(pIpv6Addr->au16[idx])));
197 }
198
199 if (fEmbeddedIpv4)
200 cch += pfnOutput(pvArgOutput, ":", 1);
201 }
202 }
203
204 if (fEmbeddedIpv4)
205 cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
206 "%u.%u.%u.%u",
207 pIpv6Addr->au8[12],
208 pIpv6Addr->au8[13],
209 pIpv6Addr->au8[14],
210 pIpv6Addr->au8[15]);
211
212 return cch;
213}
214
215
216/**
217 * Callback to format iprt formatting extentions.
218 * See @ref pg_rt_str_format for a reference on the format types.
219 *
220 * @returns The number of bytes formatted.
221 * @param pfnOutput Pointer to output function.
222 * @param pvArgOutput Argument for the output function.
223 * @param ppszFormat Pointer to the format string pointer. Advance this till the char
224 * after the format specifier.
225 * @param pArgs Pointer to the argument list. Use this to fetch the arguments.
226 * @param cchWidth Format Width. -1 if not specified.
227 * @param cchPrecision Format Precision. -1 if not specified.
228 * @param fFlags Flags (RTSTR_NTFS_*).
229 * @param chArgSize The argument size specifier, 'l' or 'L'.
230 */
231DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, const char **ppszFormat, va_list *pArgs,
232 int cchWidth, int cchPrecision, unsigned fFlags, char chArgSize)
233{
234 const char *pszFormatOrg = *ppszFormat;
235 char ch = *(*ppszFormat)++;
236 size_t cch;
237 char szBuf[80];
238
239 if (ch == 'R')
240 {
241 ch = *(*ppszFormat)++;
242 switch (ch)
243 {
244 /*
245 * Groups 1 and 2.
246 */
247 case 'T':
248 case 'G':
249 case 'H':
250 case 'R':
251 case 'C':
252 case 'I':
253 case 'X':
254 case 'U':
255 case 'K':
256 {
257 /*
258 * Interpret the type.
259 */
260 typedef enum
261 {
262 RTSF_INT,
263 RTSF_INTW,
264 RTSF_BOOL,
265 RTSF_FP16,
266 RTSF_FP32,
267 RTSF_FP64,
268 RTSF_IPV4,
269 RTSF_IPV6,
270 RTSF_MAC,
271 RTSF_NETADDR,
272 RTSF_UUID
273 } RTSF;
274 static const struct
275 {
276 uint8_t cch; /**< the length of the string. */
277 char sz[10]; /**< the part following 'R'. */
278 uint8_t cb; /**< the size of the type. */
279 uint8_t u8Base; /**< the size of the type. */
280 RTSF enmFormat; /**< The way to format it. */
281 uint16_t fFlags; /**< additional RTSTR_F_* flags. */
282 }
283 /** Sorted array of types, looked up using binary search! */
284 s_aTypes[] =
285 {
286#define STRMEM(str) sizeof(str) - 1, str
287 { STRMEM("Ci"), sizeof(RTINT), 10, RTSF_INT, RTSTR_F_VALSIGNED },
288 { STRMEM("Cp"), sizeof(RTCCPHYS), 16, RTSF_INTW, 0 },
289 { STRMEM("Cr"), sizeof(RTCCUINTREG), 16, RTSF_INTW, 0 },
290 { STRMEM("Cu"), sizeof(RTUINT), 10, RTSF_INT, 0 },
291 { STRMEM("Cv"), sizeof(void *), 16, RTSF_INTW, 0 },
292 { STRMEM("Cx"), sizeof(RTUINT), 16, RTSF_INT, 0 },
293 { STRMEM("Gi"), sizeof(RTGCINT), 10, RTSF_INT, RTSTR_F_VALSIGNED },
294 { STRMEM("Gp"), sizeof(RTGCPHYS), 16, RTSF_INTW, 0 },
295 { STRMEM("Gr"), sizeof(RTGCUINTREG), 16, RTSF_INTW, 0 },
296 { STRMEM("Gu"), sizeof(RTGCUINT), 10, RTSF_INT, 0 },
297 { STRMEM("Gv"), sizeof(RTGCPTR), 16, RTSF_INTW, 0 },
298 { STRMEM("Gx"), sizeof(RTGCUINT), 16, RTSF_INT, 0 },
299 { STRMEM("Hi"), sizeof(RTHCINT), 10, RTSF_INT, RTSTR_F_VALSIGNED },
300 { STRMEM("Hp"), sizeof(RTHCPHYS), 16, RTSF_INTW, 0 },
301 { STRMEM("Hr"), sizeof(RTHCUINTREG), 16, RTSF_INTW, 0 },
302 { STRMEM("Hu"), sizeof(RTHCUINT), 10, RTSF_INT, 0 },
303 { STRMEM("Hv"), sizeof(RTHCPTR), 16, RTSF_INTW, 0 },
304 { STRMEM("Hx"), sizeof(RTHCUINT), 16, RTSF_INT, 0 },
305 { STRMEM("I16"), sizeof(int16_t), 10, RTSF_INT, RTSTR_F_VALSIGNED },
306 { STRMEM("I32"), sizeof(int32_t), 10, RTSF_INT, RTSTR_F_VALSIGNED },
307 { STRMEM("I64"), sizeof(int64_t), 10, RTSF_INT, RTSTR_F_VALSIGNED },
308 { STRMEM("I8"), sizeof(int8_t), 10, RTSF_INT, RTSTR_F_VALSIGNED },
309 { STRMEM("Kv"), sizeof(RTHCPTR), 16, RTSF_INT, RTSTR_F_OBFUSCATE_PTR },
310 { STRMEM("Rv"), sizeof(RTRCPTR), 16, RTSF_INTW, 0 },
311 { STRMEM("Tbool"), sizeof(bool), 10, RTSF_BOOL, 0 },
312 { STRMEM("Tfile"), sizeof(RTFILE), 10, RTSF_INT, 0 },
313 { STRMEM("Tfmode"), sizeof(RTFMODE), 16, RTSF_INTW, 0 },
314 { STRMEM("Tfoff"), sizeof(RTFOFF), 10, RTSF_INT, RTSTR_F_VALSIGNED },
315 { STRMEM("Tfp16"), sizeof(RTFAR16), 16, RTSF_FP16, RTSTR_F_ZEROPAD },
316 { STRMEM("Tfp32"), sizeof(RTFAR32), 16, RTSF_FP32, RTSTR_F_ZEROPAD },
317 { STRMEM("Tfp64"), sizeof(RTFAR64), 16, RTSF_FP64, RTSTR_F_ZEROPAD },
318 { STRMEM("Tgid"), sizeof(RTGID), 10, RTSF_INT, RTSTR_F_VALSIGNED },
319 { STRMEM("Tino"), sizeof(RTINODE), 16, RTSF_INTW, 0 },
320 { STRMEM("Tint"), sizeof(RTINT), 10, RTSF_INT, RTSTR_F_VALSIGNED },
321 { STRMEM("Tiop"), sizeof(RTIOPORT), 16, RTSF_INTW, 0 },
322 { STRMEM("Tldrm"), sizeof(RTLDRMOD), 16, RTSF_INTW, 0 },
323 { STRMEM("Tmac"), sizeof(PCRTMAC), 16, RTSF_MAC, 0 },
324 { STRMEM("Tnaddr"), sizeof(PCRTNETADDR), 10, RTSF_NETADDR,0 },
325 { STRMEM("Tnaipv4"), sizeof(RTNETADDRIPV4), 10, RTSF_IPV4, 0 },
326 { STRMEM("Tnaipv6"), sizeof(PCRTNETADDRIPV6),16, RTSF_IPV6, 0 },
327 { STRMEM("Tnthrd"), sizeof(RTNATIVETHREAD), 16, RTSF_INTW, 0 },
328 { STRMEM("Tproc"), sizeof(RTPROCESS), 16, RTSF_INTW, 0 },
329 { STRMEM("Tptr"), sizeof(RTUINTPTR), 16, RTSF_INTW, 0 },
330 { STRMEM("Treg"), sizeof(RTCCUINTREG), 16, RTSF_INTW, 0 },
331 { STRMEM("Tsel"), sizeof(RTSEL), 16, RTSF_INTW, 0 },
332 { STRMEM("Tsem"), sizeof(RTSEMEVENT), 16, RTSF_INTW, 0 },
333 { STRMEM("Tsock"), sizeof(RTSOCKET), 10, RTSF_INT, 0 },
334 { STRMEM("Tthrd"), sizeof(RTTHREAD), 16, RTSF_INTW, 0 },
335 { STRMEM("Tuid"), sizeof(RTUID), 10, RTSF_INT, RTSTR_F_VALSIGNED },
336 { STRMEM("Tuint"), sizeof(RTUINT), 10, RTSF_INT, 0 },
337 { STRMEM("Tunicp"), sizeof(RTUNICP), 16, RTSF_INTW, RTSTR_F_ZEROPAD },
338 { STRMEM("Tutf16"), sizeof(RTUTF16), 16, RTSF_INTW, RTSTR_F_ZEROPAD },
339 { STRMEM("Tuuid"), sizeof(PCRTUUID), 16, RTSF_UUID, 0 },
340 { STRMEM("Txint"), sizeof(RTUINT), 16, RTSF_INT, 0 },
341 { STRMEM("U16"), sizeof(uint16_t), 10, RTSF_INT, 0 },
342 { STRMEM("U32"), sizeof(uint32_t), 10, RTSF_INT, 0 },
343 { STRMEM("U64"), sizeof(uint64_t), 10, RTSF_INT, 0 },
344 { STRMEM("U8"), sizeof(uint8_t), 10, RTSF_INT, 0 },
345 { STRMEM("X16"), sizeof(uint16_t), 16, RTSF_INT, 0 },
346 { STRMEM("X32"), sizeof(uint32_t), 16, RTSF_INT, 0 },
347 { STRMEM("X64"), sizeof(uint64_t), 16, RTSF_INT, 0 },
348 { STRMEM("X8"), sizeof(uint8_t), 16, RTSF_INT, 0 },
349#undef STRMEM
350 };
351 static const char s_szNull[] = "<NULL>";
352
353 const char *pszType = *ppszFormat - 1;
354 int iStart = 0;
355 int iEnd = RT_ELEMENTS(s_aTypes) - 1;
356 int i = RT_ELEMENTS(s_aTypes) / 2;
357
358 union
359 {
360 uint8_t u8;
361 uint16_t u16;
362 uint32_t u32;
363 uint64_t u64;
364 int8_t i8;
365 int16_t i16;
366 int32_t i32;
367 int64_t i64;
368 RTR0INTPTR uR0Ptr;
369 RTFAR16 fp16;
370 RTFAR32 fp32;
371 RTFAR64 fp64;
372 bool fBool;
373 PCRTMAC pMac;
374 RTNETADDRIPV4 Ipv4Addr;
375 PCRTNETADDRIPV6 pIpv6Addr;
376 PCRTNETADDR pNetAddr;
377 PCRTUUID pUuid;
378 } u;
379
380 AssertMsg(!chArgSize, ("Not argument size '%c' for RT types! '%.10s'\n", chArgSize, pszFormatOrg));
381 RT_NOREF_PV(chArgSize);
382
383 /*
384 * Lookup the type - binary search.
385 */
386 for (;;)
387 {
388 int iDiff = strncmp(pszType, s_aTypes[i].sz, s_aTypes[i].cch);
389 if (!iDiff)
390 break;
391 if (iEnd == iStart)
392 {
393 AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg));
394 return 0;
395 }
396 if (iDiff < 0)
397 iEnd = i - 1;
398 else
399 iStart = i + 1;
400 if (iEnd < iStart)
401 {
402 AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg));
403 return 0;
404 }
405 i = iStart + (iEnd - iStart) / 2;
406 }
407
408 /*
409 * Advance the format string and merge flags.
410 */
411 *ppszFormat += s_aTypes[i].cch - 1;
412 fFlags |= s_aTypes[i].fFlags;
413
414 /*
415 * Fetch the argument.
416 * It's important that a signed value gets sign-extended up to 64-bit.
417 */
418 RT_ZERO(u);
419 if (fFlags & RTSTR_F_VALSIGNED)
420 {
421 switch (s_aTypes[i].cb)
422 {
423 case sizeof(int8_t):
424 u.i64 = va_arg(*pArgs, /*int8_t*/int);
425 fFlags |= RTSTR_F_8BIT;
426 break;
427 case sizeof(int16_t):
428 u.i64 = va_arg(*pArgs, /*int16_t*/int);
429 fFlags |= RTSTR_F_16BIT;
430 break;
431 case sizeof(int32_t):
432 u.i64 = va_arg(*pArgs, int32_t);
433 fFlags |= RTSTR_F_32BIT;
434 break;
435 case sizeof(int64_t):
436 u.i64 = va_arg(*pArgs, int64_t);
437 fFlags |= RTSTR_F_64BIT;
438 break;
439 default:
440 AssertMsgFailed(("Invalid format error, size %d'!\n", s_aTypes[i].cb));
441 break;
442 }
443 }
444 else
445 {
446 switch (s_aTypes[i].cb)
447 {
448 case sizeof(uint8_t):
449 u.u8 = va_arg(*pArgs, /*uint8_t*/unsigned);
450 fFlags |= RTSTR_F_8BIT;
451 break;
452 case sizeof(uint16_t):
453 u.u16 = va_arg(*pArgs, /*uint16_t*/unsigned);
454 fFlags |= RTSTR_F_16BIT;
455 break;
456 case sizeof(uint32_t):
457 u.u32 = va_arg(*pArgs, uint32_t);
458 fFlags |= RTSTR_F_32BIT;
459 break;
460 case sizeof(uint64_t):
461 u.u64 = va_arg(*pArgs, uint64_t);
462 fFlags |= RTSTR_F_64BIT;
463 break;
464 case sizeof(RTFAR32):
465 u.fp32 = va_arg(*pArgs, RTFAR32);
466 break;
467 case sizeof(RTFAR64):
468 u.fp64 = va_arg(*pArgs, RTFAR64);
469 break;
470 default:
471 AssertMsgFailed(("Invalid format error, size %d'!\n", s_aTypes[i].cb));
472 break;
473 }
474 }
475
476#ifndef DEBUG
477 /*
478 * For now don't show the address.
479 */
480 if (fFlags & RTSTR_F_OBFUSCATE_PTR)
481 {
482 cch = rtStrFormatKernelAddress(szBuf, sizeof(szBuf), u.uR0Ptr, cchWidth, cchPrecision, fFlags);
483 return pfnOutput(pvArgOutput, szBuf, cch);
484 }
485#endif
486
487 /*
488 * Format the output.
489 */
490 switch (s_aTypes[i].enmFormat)
491 {
492 case RTSF_INT:
493 {
494 cch = RTStrFormatNumber(szBuf, u.u64, s_aTypes[i].u8Base, cchWidth, cchPrecision, fFlags);
495 break;
496 }
497
498 /* hex which defaults to max width. */
499 case RTSF_INTW:
500 {
501 Assert(s_aTypes[i].u8Base == 16);
502 if (cchWidth < 0)
503 {
504 cchWidth = s_aTypes[i].cb * 2 + (fFlags & RTSTR_F_SPECIAL ? 2 : 0);
505 fFlags |= RTSTR_F_ZEROPAD;
506 }
507 cch = RTStrFormatNumber(szBuf, u.u64, s_aTypes[i].u8Base, cchWidth, cchPrecision, fFlags);
508 break;
509 }
510
511 case RTSF_BOOL:
512 {
513 static const char s_szTrue[] = "true ";
514 static const char s_szFalse[] = "false";
515 if (u.u64 == 1)
516 return pfnOutput(pvArgOutput, s_szTrue, sizeof(s_szTrue) - 1);
517 if (u.u64 == 0)
518 return pfnOutput(pvArgOutput, s_szFalse, sizeof(s_szFalse) - 1);
519 /* invalid boolean value */
520 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "!%lld!", u.u64);
521 }
522
523 case RTSF_FP16:
524 {
525 fFlags &= ~(RTSTR_F_VALSIGNED | RTSTR_F_BIT_MASK | RTSTR_F_WIDTH | RTSTR_F_PRECISION | RTSTR_F_THOUSAND_SEP);
526 cch = RTStrFormatNumber(&szBuf[0], u.fp16.sel, 16, 4, -1, fFlags | RTSTR_F_16BIT);
527 Assert(cch == 4);
528 szBuf[4] = ':';
529 cch = RTStrFormatNumber(&szBuf[5], u.fp16.off, 16, 4, -1, fFlags | RTSTR_F_16BIT);
530 Assert(cch == 4);
531 cch = 4 + 1 + 4;
532 break;
533 }
534 case RTSF_FP32:
535 {
536 fFlags &= ~(RTSTR_F_VALSIGNED | RTSTR_F_BIT_MASK | RTSTR_F_WIDTH | RTSTR_F_PRECISION | RTSTR_F_THOUSAND_SEP);
537 cch = RTStrFormatNumber(&szBuf[0], u.fp32.sel, 16, 4, -1, fFlags | RTSTR_F_16BIT);
538 Assert(cch == 4);
539 szBuf[4] = ':';
540 cch = RTStrFormatNumber(&szBuf[5], u.fp32.off, 16, 8, -1, fFlags | RTSTR_F_32BIT);
541 Assert(cch == 8);
542 cch = 4 + 1 + 8;
543 break;
544 }
545 case RTSF_FP64:
546 {
547 fFlags &= ~(RTSTR_F_VALSIGNED | RTSTR_F_BIT_MASK | RTSTR_F_WIDTH | RTSTR_F_PRECISION | RTSTR_F_THOUSAND_SEP);
548 cch = RTStrFormatNumber(&szBuf[0], u.fp64.sel, 16, 4, -1, fFlags | RTSTR_F_16BIT);
549 Assert(cch == 4);
550 szBuf[4] = ':';
551 cch = RTStrFormatNumber(&szBuf[5], u.fp64.off, 16, 16, -1, fFlags | RTSTR_F_64BIT);
552 Assert(cch == 16);
553 cch = 4 + 1 + 16;
554 break;
555 }
556
557 case RTSF_IPV4:
558 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
559 "%u.%u.%u.%u",
560 u.Ipv4Addr.au8[0],
561 u.Ipv4Addr.au8[1],
562 u.Ipv4Addr.au8[2],
563 u.Ipv4Addr.au8[3]);
564
565 case RTSF_IPV6:
566 {
567 if (VALID_PTR(u.pIpv6Addr))
568 return rtstrFormatIPv6(pfnOutput, pvArgOutput, u.pIpv6Addr);
569 return pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1);
570 }
571
572 case RTSF_MAC:
573 {
574 if (VALID_PTR(u.pMac))
575 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
576 "%02x:%02x:%02x:%02x:%02x:%02x",
577 u.pMac->au8[0],
578 u.pMac->au8[1],
579 u.pMac->au8[2],
580 u.pMac->au8[3],
581 u.pMac->au8[4],
582 u.pMac->au8[5]);
583 return pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1);
584 }
585
586 case RTSF_NETADDR:
587 {
588 if (VALID_PTR(u.pNetAddr))
589 {
590 switch (u.pNetAddr->enmType)
591 {
592 case RTNETADDRTYPE_IPV4:
593 if (u.pNetAddr->uPort == RTNETADDR_PORT_NA)
594 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
595 "%u.%u.%u.%u",
596 u.pNetAddr->uAddr.IPv4.au8[0],
597 u.pNetAddr->uAddr.IPv4.au8[1],
598 u.pNetAddr->uAddr.IPv4.au8[2],
599 u.pNetAddr->uAddr.IPv4.au8[3]);
600 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
601 "%u.%u.%u.%u:%u",
602 u.pNetAddr->uAddr.IPv4.au8[0],
603 u.pNetAddr->uAddr.IPv4.au8[1],
604 u.pNetAddr->uAddr.IPv4.au8[2],
605 u.pNetAddr->uAddr.IPv4.au8[3],
606 u.pNetAddr->uPort);
607
608 case RTNETADDRTYPE_IPV6:
609 if (u.pNetAddr->uPort == RTNETADDR_PORT_NA)
610 return rtstrFormatIPv6(pfnOutput, pvArgOutput, &u.pNetAddr->uAddr.IPv6);
611
612 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
613 "[%RTnaipv6]:%u",
614 &u.pNetAddr->uAddr.IPv6,
615 u.pNetAddr->uPort);
616
617 case RTNETADDRTYPE_MAC:
618 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
619 "%02x:%02x:%02x:%02x:%02x:%02x",
620 u.pNetAddr->uAddr.Mac.au8[0],
621 u.pNetAddr->uAddr.Mac.au8[1],
622 u.pNetAddr->uAddr.Mac.au8[2],
623 u.pNetAddr->uAddr.Mac.au8[3],
624 u.pNetAddr->uAddr.Mac.au8[4],
625 u.pNetAddr->uAddr.Mac.au8[5]);
626
627 default:
628 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
629 "unsupported-netaddr-type=%u", u.pNetAddr->enmType);
630
631 }
632 }
633 return pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1);
634 }
635
636 case RTSF_UUID:
637 {
638 if (VALID_PTR(u.pUuid))
639 {
640 /* cannot call RTUuidToStr because of GC/R0. */
641 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
642 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
643 RT_H2LE_U32(u.pUuid->Gen.u32TimeLow),
644 RT_H2LE_U16(u.pUuid->Gen.u16TimeMid),
645 RT_H2LE_U16(u.pUuid->Gen.u16TimeHiAndVersion),
646 u.pUuid->Gen.u8ClockSeqHiAndReserved,
647 u.pUuid->Gen.u8ClockSeqLow,
648 u.pUuid->Gen.au8Node[0],
649 u.pUuid->Gen.au8Node[1],
650 u.pUuid->Gen.au8Node[2],
651 u.pUuid->Gen.au8Node[3],
652 u.pUuid->Gen.au8Node[4],
653 u.pUuid->Gen.au8Node[5]);
654 }
655 return pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1);
656 }
657
658 default:
659 AssertMsgFailed(("Internal error %d\n", s_aTypes[i].enmFormat));
660 return 0;
661 }
662
663 /*
664 * Finally, output the formatted string and return.
665 */
666 return pfnOutput(pvArgOutput, szBuf, cch);
667 }
668
669
670 /* Group 3 */
671
672 /*
673 * Base name printing, big endian UTF-16.
674 */
675 case 'b':
676 {
677 switch (*(*ppszFormat)++)
678 {
679 case 'n':
680 {
681 const char *pszLastSep;
682 const char *psz = pszLastSep = va_arg(*pArgs, const char *);
683 if (!VALID_PTR(psz))
684 return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>"));
685
686 while ((ch = *psz) != '\0')
687 {
688 if (RTPATH_IS_SEP(ch))
689 {
690 do
691 psz++;
692 while ((ch = *psz) != '\0' && RTPATH_IS_SEP(ch));
693 if (!ch)
694 break;
695 pszLastSep = psz;
696 }
697 psz++;
698 }
699
700 return pfnOutput(pvArgOutput, pszLastSep, psz - pszLastSep);
701 }
702
703 /* %lRbs */
704 case 's':
705 if (chArgSize == 'l')
706 {
707 /* utf-16BE -> utf-8 */
708 int cchStr;
709 PCRTUTF16 pwszStr = va_arg(*pArgs, PRTUTF16);
710
711 if (RT_VALID_PTR(pwszStr))
712 {
713 cchStr = 0;
714 while (cchStr < cchPrecision && pwszStr[cchStr] != '\0')
715 cchStr++;
716 }
717 else
718 {
719 static RTUTF16 s_wszBigNull[] =
720 {
721 RT_H2BE_U16_C((uint16_t)'<'), RT_H2BE_U16_C((uint16_t)'N'), RT_H2BE_U16_C((uint16_t)'U'),
722 RT_H2BE_U16_C((uint16_t)'L'), RT_H2BE_U16_C((uint16_t)'L'), RT_H2BE_U16_C((uint16_t)'>'), '\0'
723 };
724 pwszStr = s_wszBigNull;
725 cchStr = RT_ELEMENTS(s_wszBigNull) - 1;
726 }
727
728 cch = 0;
729 if (!(fFlags & RTSTR_F_LEFT))
730 while (--cchWidth >= cchStr)
731 cch += pfnOutput(pvArgOutput, " ", 1);
732 cchWidth -= cchStr;
733 while (cchStr-- > 0)
734 {
735/** @todo \#ifndef IN_RC*/
736#ifdef IN_RING3
737 RTUNICP Cp = 0;
738 RTUtf16BigGetCpEx(&pwszStr, &Cp);
739 char *pszEnd = RTStrPutCp(szBuf, Cp);
740 *pszEnd = '\0';
741 cch += pfnOutput(pvArgOutput, szBuf, pszEnd - szBuf);
742#else
743 szBuf[0] = (char)(*pwszStr++ >> 8);
744 cch += pfnOutput(pvArgOutput, szBuf, 1);
745#endif
746 }
747 while (--cchWidth >= 0)
748 cch += pfnOutput(pvArgOutput, " ", 1);
749 return cch;
750 }
751 RT_FALL_THRU();
752
753 default:
754 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
755 break;
756 }
757 break;
758 }
759
760
761 /*
762 * Pretty function / method name printing.
763 */
764 case 'f':
765 {
766 switch (*(*ppszFormat)++)
767 {
768 /*
769 * Pretty function / method name printing.
770 * This isn't 100% right (see classic signal prototype) and it assumes
771 * standardized names, but it'll do for today.
772 */
773 case 'n':
774 {
775 const char *pszStart;
776 const char *psz = pszStart = va_arg(*pArgs, const char *);
777 int cAngle = 0;
778
779 if (!VALID_PTR(psz))
780 return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>"));
781
782 while ((ch = *psz) != '\0' && ch != '(')
783 {
784 if (RT_C_IS_BLANK(ch))
785 {
786 psz++;
787 while ((ch = *psz) != '\0' && (RT_C_IS_BLANK(ch) || ch == '('))
788 psz++;
789 if (ch && cAngle == 0)
790 pszStart = psz;
791 }
792 else if (ch == '(')
793 break;
794 else if (ch == '<')
795 {
796 cAngle++;
797 psz++;
798 }
799 else if (ch == '>')
800 {
801 cAngle--;
802 psz++;
803 }
804 else
805 psz++;
806 }
807
808 return pfnOutput(pvArgOutput, pszStart, psz - pszStart);
809 }
810
811 default:
812 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
813 break;
814 }
815 break;
816 }
817
818
819 /*
820 * hex dumping, COM/XPCOM, human readable sizes.
821 */
822 case 'h':
823 {
824 ch = *(*ppszFormat)++;
825 switch (ch)
826 {
827 /*
828 * Hex stuff.
829 */
830 case 'x':
831 {
832 uint8_t *pu8 = va_arg(*pArgs, uint8_t *);
833 if (cchPrecision < 0)
834 cchPrecision = 16;
835 if (pu8)
836 {
837 switch (*(*ppszFormat)++)
838 {
839 /*
840 * Regular hex dump.
841 */
842 case 'd':
843 {
844 int off = 0;
845 cch = 0;
846
847 if (cchWidth <= 0)
848 cchWidth = 16;
849
850 while (off < cchPrecision)
851 {
852 int i;
853 cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
854 "%s%0*p %04x:", off ? "\n" : "", sizeof(pu8) * 2, (uintptr_t)pu8, off);
855 for (i = 0; i < cchWidth && off + i < cchPrecision ; i++)
856 cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
857 off + i < cchPrecision ? !(i & 7) && i ? "-%02x" : " %02x" : " ",
858 pu8[i]);
859 while (i++ < cchWidth)
860 cch += pfnOutput(pvArgOutput, " ", 3);
861
862 cch += pfnOutput(pvArgOutput, " ", 1);
863
864 for (i = 0; i < cchWidth && off + i < cchPrecision; i++)
865 {
866 uint8_t u8 = pu8[i];
867 cch += pfnOutput(pvArgOutput, u8 < 127 && u8 >= 32 ? (const char *)&u8 : ".", 1);
868 }
869
870 /* next */
871 pu8 += cchWidth;
872 off += cchWidth;
873 }
874 return cch;
875 }
876
877 /*
878 * Regular hex dump with dittoing.
879 */
880 case 'D':
881 {
882 int offEndDupCheck;
883 int cDuplicates = 0;
884 int off = 0;
885 cch = 0;
886
887 if (cchWidth <= 0)
888 cchWidth = 16;
889 offEndDupCheck = cchPrecision - cchWidth;
890
891 while (off < cchPrecision)
892 {
893 int i;
894 if ( off >= offEndDupCheck
895 || off <= 0
896 || memcmp(pu8, pu8 - cchWidth, cchWidth) != 0
897 || ( cDuplicates == 0
898 && ( off + cchWidth >= offEndDupCheck
899 || memcmp(pu8 + cchWidth, pu8, cchWidth) != 0)) )
900 {
901 if (cDuplicates > 0)
902 {
903 cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
904 "\n%.*s **** <ditto x %u>",
905 sizeof(pu8) * 2, "****************", cDuplicates);
906 cDuplicates = 0;
907 }
908
909 cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
910 "%s%0*p %04x:", off ? "\n" : "", sizeof(pu8) * 2, (uintptr_t)pu8, off);
911 for (i = 0; i < cchWidth && off + i < cchPrecision ; i++)
912 cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
913 off + i < cchPrecision ? !(i & 7) && i
914 ? "-%02x" : " %02x" : " ",
915 pu8[i]);
916 while (i++ < cchWidth)
917 cch += pfnOutput(pvArgOutput, " ", 3);
918
919 cch += pfnOutput(pvArgOutput, " ", 1);
920
921 for (i = 0; i < cchWidth && off + i < cchPrecision; i++)
922 {
923 uint8_t u8 = pu8[i];
924 cch += pfnOutput(pvArgOutput, u8 < 127 && u8 >= 32 ? (const char *)&u8 : ".", 1);
925 }
926 }
927 else
928 cDuplicates++;
929
930 /* next */
931 pu8 += cchWidth;
932 off += cchWidth;
933 }
934 return cch;
935 }
936
937 /*
938 * Hex string.
939 */
940 case 's':
941 {
942 if (cchPrecision-- > 0)
943 {
944 cch = RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%02x", *pu8++);
945 for (; cchPrecision > 0; cchPrecision--, pu8++)
946 cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, " %02x", *pu8);
947 return cch;
948 }
949 break;
950 }
951
952 default:
953 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
954 break;
955 }
956 }
957 else
958 return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>"));
959 break;
960 }
961
962
963#ifdef IN_RING3
964 /*
965 * XPCOM / COM status code: %Rhrc, %Rhrf, %Rhra
966 * ASSUMES: If Windows Then COM else XPCOM.
967 */
968 case 'r':
969 {
970 uint32_t hrc = va_arg(*pArgs, uint32_t);
971 PCRTCOMERRMSG pMsg = RTErrCOMGet(hrc);
972 switch (*(*ppszFormat)++)
973 {
974 case 'c':
975 return pfnOutput(pvArgOutput, pMsg->pszDefine, strlen(pMsg->pszDefine));
976 case 'f':
977 return pfnOutput(pvArgOutput, pMsg->pszMsgFull,strlen(pMsg->pszMsgFull));
978 case 'a':
979 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s (0x%08X) - %s", pMsg->pszDefine, hrc, pMsg->pszMsgFull);
980 default:
981 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
982 return 0;
983 }
984 break;
985 }
986#endif /* IN_RING3 */
987
988 case 'c':
989 case 'u':
990 {
991 unsigned i;
992 ssize_t cchBuf;
993 uint64_t uValue;
994 uint64_t uFraction = 0;
995 const char *pszPrefix = NULL;
996 unsigned cchFixedPart;
997 char ch2 = *(*ppszFormat)++;
998 AssertMsgReturn(ch2 == 'b' || ch2 == 'i', ("invalid type '%.10s'!\n", pszFormatOrg), 0);
999 uValue = va_arg(*pArgs, uint64_t);
1000
1001 if (!(fFlags & RTSTR_F_PRECISION))
1002 cchPrecision = 1;
1003 else if (cchPrecision > 3)
1004 cchPrecision = 3;
1005 else if (cchPrecision < 0)
1006 cchPrecision = 0;
1007
1008 cchFixedPart = cchPrecision + (cchPrecision != 0) + (ch == 'c');
1009
1010 if (ch2 == 'b')
1011 {
1012 static const struct
1013 {
1014 const char *pszPrefix;
1015 uint8_t cShift;
1016 uint64_t cbMin;
1017 uint64_t cbMinZeroPrecision;
1018 } s_aUnits[] =
1019 {
1020 { "Ei", 60, _1E, _1E*2 },
1021 { "Pi", 50, _1P, _1P*2 },
1022 { "Ti", 40, _1T, _1T*2 },
1023 { "Gi", 30, _1G, _1G64*2 },
1024 { "Mi", 20, _1M, _1M*2 },
1025 { "Ki", 10, _1K, _1K*2 },
1026 };
1027 for (i = 0; i < RT_ELEMENTS(s_aUnits); i++)
1028 if ( uValue >= s_aUnits[i].cbMin
1029 && (cchPrecision > 0 || uValue >= s_aUnits[i].cbMinZeroPrecision))
1030 {
1031 if (cchPrecision != 0)
1032 {
1033 uFraction = uValue & (RT_BIT_64(s_aUnits[i].cShift) - 1);
1034 uFraction *= cchPrecision == 1 ? 10 : cchPrecision == 2 ? 100 : 1000;
1035 uFraction >>= s_aUnits[i].cShift;
1036 }
1037 uValue >>= s_aUnits[i].cShift;
1038 pszPrefix = s_aUnits[i].pszPrefix;
1039 cchFixedPart += 2;
1040 break;
1041 }
1042 }
1043 else
1044 {
1045 static const struct
1046 {
1047 const char *pszPrefix;
1048 uint64_t cbFactor;
1049 uint64_t cbMinZeroPrecision;
1050 } s_aUnits[] =
1051 {
1052 { "E", UINT64_C(1000000000000000000), UINT64_C(1010000000000000000), },
1053 { "P", UINT64_C(1000000000000000), UINT64_C(1010000000000000), },
1054 { "T", UINT64_C(1000000000000), UINT64_C(1010000000000), },
1055 { "G", UINT64_C(1000000000), UINT64_C(1010000000), },
1056 { "M", UINT64_C(1000000), UINT64_C(1010000), },
1057 { "k", UINT64_C(1000), UINT64_C(1010), },
1058 };
1059 for (i = 0; i < RT_ELEMENTS(s_aUnits); i++)
1060 if ( uValue >= s_aUnits[i].cbFactor
1061 && (cchPrecision > 0 || uValue >= s_aUnits[i].cbMinZeroPrecision))
1062 {
1063 if (cchPrecision == 0)
1064 uValue /= s_aUnits[i].cbFactor;
1065 else
1066 {
1067 uFraction = uValue % s_aUnits[i].cbFactor;
1068 uValue = uValue / s_aUnits[i].cbFactor;
1069 uFraction *= cchPrecision == 1 ? 10 : cchPrecision == 2 ? 100 : 1000;
1070 uFraction += s_aUnits[i].cbFactor >> 1;
1071 uFraction /= s_aUnits[i].cbFactor;
1072 }
1073 pszPrefix = s_aUnits[i].pszPrefix;
1074 cchFixedPart += 1;
1075 break;
1076 }
1077 }
1078
1079 cchBuf = RTStrFormatU64(szBuf, sizeof(szBuf), uValue, 10, 0, 0, 0);
1080 if (pszPrefix)
1081 {
1082 if (cchPrecision)
1083 {
1084 szBuf[cchBuf++] = '.';
1085 cchBuf += RTStrFormatU64(&szBuf[cchBuf], sizeof(szBuf) - cchBuf, uFraction, 10, cchPrecision, 0,
1086 RTSTR_F_ZEROPAD | RTSTR_F_WIDTH);
1087 }
1088 szBuf[cchBuf++] = *pszPrefix++;
1089 if (*pszPrefix)
1090 szBuf[cchBuf++] = *pszPrefix;
1091 }
1092 if (ch == 'c')
1093 szBuf[cchBuf++] = 'B';
1094 szBuf[cchBuf] = '\0';
1095
1096 cch = 0;
1097 if ((fFlags & RTSTR_F_WIDTH) && !(fFlags & RTSTR_F_LEFT))
1098 while (cchBuf < cchWidth)
1099 {
1100 cch += pfnOutput(pvArgOutput, fFlags & RTSTR_F_ZEROPAD ? "0" : " ", 1);
1101 cchWidth--;
1102 }
1103 cch += pfnOutput(pvArgOutput, szBuf, cchBuf);
1104 return cch;
1105 }
1106
1107 default:
1108 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
1109 return 0;
1110
1111 }
1112 break;
1113 }
1114
1115 /*
1116 * iprt status code: %Rrc, %Rrs, %Rrf, %Rra.
1117 */
1118 case 'r':
1119 {
1120 int rc = va_arg(*pArgs, int);
1121#ifdef IN_RING3 /* we don't want this anywhere else yet. */
1122 PCRTSTATUSMSG pMsg = RTErrGet(rc);
1123 switch (*(*ppszFormat)++)
1124 {
1125 case 'c':
1126 return pfnOutput(pvArgOutput, pMsg->pszDefine, strlen(pMsg->pszDefine));
1127 case 's':
1128 return pfnOutput(pvArgOutput, pMsg->pszMsgShort, strlen(pMsg->pszMsgShort));
1129 case 'f':
1130 return pfnOutput(pvArgOutput, pMsg->pszMsgFull, strlen(pMsg->pszMsgFull));
1131 case 'a':
1132 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s (%d) - %s", pMsg->pszDefine, rc, pMsg->pszMsgFull);
1133 default:
1134 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
1135 return 0;
1136 }
1137#else /* !IN_RING3 */
1138 switch (*(*ppszFormat)++)
1139 {
1140 case 'c':
1141 case 's':
1142 case 'f':
1143 case 'a':
1144 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%d", rc);
1145 default:
1146 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
1147 return 0;
1148 }
1149#endif /* !IN_RING3 */
1150 break;
1151 }
1152
1153#if defined(IN_RING3)
1154 /*
1155 * Windows status code: %Rwc, %Rwf, %Rwa
1156 */
1157 case 'w':
1158 {
1159 long rc = va_arg(*pArgs, long);
1160# if defined(RT_OS_WINDOWS)
1161 PCRTWINERRMSG pMsg = RTErrWinGet(rc);
1162# endif
1163 switch (*(*ppszFormat)++)
1164 {
1165# if defined(RT_OS_WINDOWS)
1166 case 'c':
1167 return pfnOutput(pvArgOutput, pMsg->pszDefine, strlen(pMsg->pszDefine));
1168 case 'f':
1169 return pfnOutput(pvArgOutput, pMsg->pszMsgFull,strlen(pMsg->pszMsgFull));
1170 case 'a':
1171 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s (0x%08X) - %s", pMsg->pszDefine, rc, pMsg->pszMsgFull);
1172# else
1173 case 'c':
1174 case 'f':
1175 case 'a':
1176 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "0x%08X", rc);
1177# endif
1178 default:
1179 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg));
1180 return 0;
1181 }
1182 break;
1183 }
1184#endif /* IN_RING3 */
1185
1186 /*
1187 * Group 4, structure dumpers.
1188 */
1189 case 'D':
1190 {
1191 /*
1192 * Interpret the type.
1193 */
1194 typedef enum
1195 {
1196 RTST_TIMESPEC
1197 } RTST;
1198/** Set if it's a pointer */
1199#define RTST_FLAGS_POINTER RT_BIT(0)
1200 static const struct
1201 {
1202 uint8_t cch; /**< the length of the string. */
1203 char sz[16-2]; /**< the part following 'R'. */
1204 uint8_t cb; /**< the size of the argument. */
1205 uint8_t fFlags; /**< RTST_FLAGS_* */
1206 RTST enmType; /**< The structure type. */
1207 }
1208 /** Sorted array of types, looked up using binary search! */
1209 s_aTypes[] =
1210 {
1211#define STRMEM(str) sizeof(str) - 1, str
1212 { STRMEM("Dtimespec"), sizeof(PCRTTIMESPEC), RTST_FLAGS_POINTER, RTST_TIMESPEC},
1213#undef STRMEM
1214 };
1215 const char *pszType = *ppszFormat - 1;
1216 int iStart = 0;
1217 int iEnd = RT_ELEMENTS(s_aTypes) - 1;
1218 int i = RT_ELEMENTS(s_aTypes) / 2;
1219
1220 union
1221 {
1222 const void *pv;
1223 uint64_t u64;
1224 PCRTTIMESPEC pTimeSpec;
1225 } u;
1226
1227 AssertMsg(!chArgSize, ("Not argument size '%c' for RT types! '%.10s'\n", chArgSize, pszFormatOrg));
1228
1229 /*
1230 * Lookup the type - binary search.
1231 */
1232 for (;;)
1233 {
1234 int iDiff = strncmp(pszType, s_aTypes[i].sz, s_aTypes[i].cch);
1235 if (!iDiff)
1236 break;
1237 if (iEnd == iStart)
1238 {
1239 AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg));
1240 return 0;
1241 }
1242 if (iDiff < 0)
1243 iEnd = i - 1;
1244 else
1245 iStart = i + 1;
1246 if (iEnd < iStart)
1247 {
1248 AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg));
1249 return 0;
1250 }
1251 i = iStart + (iEnd - iStart) / 2;
1252 }
1253 *ppszFormat += s_aTypes[i].cch - 1;
1254
1255 /*
1256 * Fetch the argument.
1257 */
1258 u.u64 = 0;
1259 switch (s_aTypes[i].cb)
1260 {
1261 case sizeof(const void *):
1262 u.pv = va_arg(*pArgs, const void *);
1263 break;
1264 default:
1265 AssertMsgFailed(("Invalid format error, size %d'!\n", s_aTypes[i].cb));
1266 break;
1267 }
1268
1269 /*
1270 * If it's a pointer, we'll check if it's valid before going on.
1271 */
1272 if ((s_aTypes[i].fFlags & RTST_FLAGS_POINTER) && !VALID_PTR(u.pv))
1273 return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>"));
1274
1275 /*
1276 * Format the output.
1277 */
1278 switch (s_aTypes[i].enmType)
1279 {
1280 case RTST_TIMESPEC:
1281 return RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL, "%'lld ns", RTTimeSpecGetNano(u.pTimeSpec));
1282
1283 default:
1284 AssertMsgFailed(("Invalid/unhandled enmType=%d\n", s_aTypes[i].enmType));
1285 break;
1286 }
1287 break;
1288 }
1289
1290#ifdef IN_RING3
1291
1292 /*
1293 * Group 5, XML / HTML escapers.
1294 */
1295 case 'M':
1296 {
1297 char chWhat = (*ppszFormat)[0];
1298 if (chWhat == 'a' || chWhat == 'e')
1299 {
1300 bool fAttr = chWhat == 'a';
1301 char chType = (*ppszFormat)[1];
1302 *ppszFormat += 2;
1303 switch (chType)
1304 {
1305 case 's':
1306 {
1307 static const char s_szElemEscape[] = "<>&\"'";
1308 static const char s_szAttrEscape[] = "<>&\"\n\r"; /* more? */
1309 const char * const pszEscape = fAttr ? s_szAttrEscape : s_szElemEscape;
1310 size_t const cchEscape = (fAttr ? RT_ELEMENTS(s_szAttrEscape) : RT_ELEMENTS(s_szElemEscape)) - 1;
1311 size_t cchOutput = 0;
1312 const char *pszStr = va_arg(*pArgs, char *);
1313 ssize_t cchStr;
1314 ssize_t offCur;
1315 ssize_t offLast;
1316
1317 if (!VALID_PTR(pszStr))
1318 pszStr = "<NULL>";
1319 cchStr = RTStrNLen(pszStr, (unsigned)cchPrecision);
1320
1321 if (fAttr)
1322 cchOutput += pfnOutput(pvArgOutput, "\"", 1);
1323 if (!(fFlags & RTSTR_F_LEFT))
1324 while (--cchWidth >= cchStr)
1325 cchOutput += pfnOutput(pvArgOutput, " ", 1);
1326
1327 offLast = offCur = 0;
1328 while (offCur < cchStr)
1329 {
1330 if (memchr(pszEscape, pszStr[offCur], cchEscape))
1331 {
1332 if (offLast < offCur)
1333 cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);
1334 switch (pszStr[offCur])
1335 {
1336 case '<': cchOutput += pfnOutput(pvArgOutput, "&lt;", 4); break;
1337 case '>': cchOutput += pfnOutput(pvArgOutput, "&gt;", 4); break;
1338 case '&': cchOutput += pfnOutput(pvArgOutput, "&amp;", 5); break;
1339 case '\'': cchOutput += pfnOutput(pvArgOutput, "&apos;", 6); break;
1340 case '"': cchOutput += pfnOutput(pvArgOutput, "&quot;", 6); break;
1341 case '\n': cchOutput += pfnOutput(pvArgOutput, "&#xA;", 5); break;
1342 case '\r': cchOutput += pfnOutput(pvArgOutput, "&#xD;", 5); break;
1343 default:
1344 AssertFailed();
1345 }
1346 offLast = offCur + 1;
1347 }
1348 offCur++;
1349 }
1350 if (offLast < offCur)
1351 cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);
1352
1353 while (--cchWidth >= cchStr)
1354 cchOutput += pfnOutput(pvArgOutput, " ", 1);
1355 if (fAttr)
1356 cchOutput += pfnOutput(pvArgOutput, "\"", 1);
1357 return cchOutput;
1358 }
1359
1360 default:
1361 AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));
1362 }
1363 }
1364 else if (chWhat == 'p')
1365 {
1366 /* Percent encoded string (RTC-3986). */
1367 char const chVariant = (*ppszFormat)[1];
1368 char const chAddSafe = chVariant == 'p' ? '/'
1369 : chVariant == 'q' ? '+' /* '+' in queries is problematic, so no escape. */
1370 : '~' /* whatever */;
1371 size_t cchOutput = 0;
1372 const char *pszStr = va_arg(*pArgs, char *);
1373 ssize_t cchStr;
1374 ssize_t offCur;
1375 ssize_t offLast;
1376
1377 *ppszFormat += 2;
1378 AssertMsgBreak(chVariant == 'p' || chVariant == 'q' || chVariant == 'f',
1379 ("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));
1380
1381 if (!VALID_PTR(pszStr))
1382 pszStr = "<NULL>";
1383 cchStr = RTStrNLen(pszStr, (unsigned)cchPrecision);
1384
1385 if (!(fFlags & RTSTR_F_LEFT))
1386 while (--cchWidth >= cchStr)
1387 cchOutput += pfnOutput(pvArgOutput, "%20", 3);
1388
1389 offLast = offCur = 0;
1390 while (offCur < cchStr)
1391 {
1392 ch = pszStr[offCur];
1393 if ( RT_C_IS_ALPHA(ch)
1394 || RT_C_IS_DIGIT(ch)
1395 || ch == '-'
1396 || ch == '.'
1397 || ch == '_'
1398 || ch == '~'
1399 || ch == chAddSafe)
1400 offCur++;
1401 else
1402 {
1403 if (offLast < offCur)
1404 cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);
1405 if (ch != ' ' || chVariant != 'f')
1406 {
1407 szBuf[0] = '%';
1408 szBuf[1] = g_szHexDigitsUpper[((uint8_t)ch >> 4) & 0xf];
1409 szBuf[2] = g_szHexDigitsUpper[(uint8_t)ch & 0xf];
1410 szBuf[3] = '\0';
1411 cchOutput += pfnOutput(pvArgOutput, szBuf, 3);
1412 }
1413 else
1414 cchOutput += pfnOutput(pvArgOutput, "+", 1);
1415 offLast = ++offCur;
1416 }
1417 }
1418 if (offLast < offCur)
1419 cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);
1420
1421 while (--cchWidth >= cchStr)
1422 cchOutput += pfnOutput(pvArgOutput, "%20", 3);
1423 }
1424 else
1425 AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));
1426 break;
1427 }
1428
1429 /*
1430 * Groups 6 - JSON.
1431 */
1432 case 'J':
1433 {
1434 char chType = (*ppszFormat)[0];
1435 *ppszFormat += 1;
1436 switch (chType)
1437 {
1438 case 's':
1439 {
1440 const char *pszStr = va_arg(*pArgs, char *);
1441 size_t cchOutput;
1442 ssize_t cchStr;
1443 ssize_t offCur;
1444 ssize_t offLast;
1445
1446 if (!VALID_PTR(pszStr))
1447 pszStr = "<NULL>";
1448 cchStr = RTStrNLen(pszStr, (unsigned)cchPrecision);
1449
1450 cchOutput = pfnOutput(pvArgOutput, "\"", 1);
1451 if (!(fFlags & RTSTR_F_LEFT))
1452 while (--cchWidth >= cchStr)
1453 cchOutput += pfnOutput(pvArgOutput, " ", 1);
1454
1455 offLast = offCur = 0;
1456 while (offCur < cchStr)
1457 {
1458 unsigned int const uch = pszStr[offCur];
1459 if ( uch >= 0x5d
1460 || (uch >= 0x20 && uch != 0x22 && uch != 0x5c))
1461 offCur++;
1462 else
1463 {
1464 if (offLast < offCur)
1465 cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);
1466 switch ((char)uch)
1467 {
1468 case '"': cchOutput += pfnOutput(pvArgOutput, "\\\"", 2); break;
1469 case '\\': cchOutput += pfnOutput(pvArgOutput, "\\\\", 2); break;
1470 case '/': cchOutput += pfnOutput(pvArgOutput, "\\/", 2); break;
1471 case '\b': cchOutput += pfnOutput(pvArgOutput, "\\b", 2); break;
1472 case '\f': cchOutput += pfnOutput(pvArgOutput, "\\f", 2); break;
1473 case '\n': cchOutput += pfnOutput(pvArgOutput, "\\n", 2); break;
1474 case '\t': cchOutput += pfnOutput(pvArgOutput, "\\t", 2); break;
1475 default:
1476 cchOutput += pfnOutput(pvArgOutput, "\\u00", 2);
1477 cchOutput += pfnOutput(pvArgOutput, &g_szHexDigits[(uch >> 4) & 0xf], 1);
1478 cchOutput += pfnOutput(pvArgOutput, &g_szHexDigits[uch & 0xf], 1);
1479 break;
1480 }
1481 offLast = ++offCur;
1482 }
1483 }
1484 if (offLast < offCur)
1485 cchOutput += pfnOutput(pvArgOutput, &pszStr[offLast], offCur - offLast);
1486
1487 while (--cchWidth >= cchStr)
1488 cchOutput += pfnOutput(pvArgOutput, " ", 1);
1489 cchOutput += pfnOutput(pvArgOutput, "\"", 1);
1490 return cchOutput;
1491 }
1492
1493 default:
1494 AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));
1495 }
1496 break;
1497 }
1498
1499#endif /* IN_RING3 */
1500
1501 /*
1502 * Groups 7 - CPU Architecture Register Formatters.
1503 * "%RAarch[reg]"
1504 */
1505 case 'A':
1506 {
1507 char const * const pszArch = *ppszFormat;
1508 const char *pszReg = pszArch;
1509 size_t cchOutput = 0;
1510 int cPrinted = 0;
1511 size_t cchReg;
1512
1513 /* Parse out the */
1514 while ((ch = *pszReg++) && ch != '[')
1515 { /* nothing */ }
1516 AssertMsgBreak(ch == '[', ("Malformed IPRT architecture register format type '%.10s'!\n", pszFormatOrg));
1517
1518 cchReg = 0;
1519 while ((ch = pszReg[cchReg]) && ch != ']')
1520 cchReg++;
1521 AssertMsgBreak(ch == ']', ("Malformed IPRT architecture register format type '%.10s'!\n", pszFormatOrg));
1522
1523 *ppszFormat = &pszReg[cchReg + 1];
1524
1525
1526#define REG_EQUALS(a_szReg) (sizeof(a_szReg) - 1 == cchReg && !strncmp(a_szReg, pszReg, sizeof(a_szReg) - 1))
1527#define REG_OUT_BIT(a_uVal, a_fBitMask, a_szName) \
1528 do { \
1529 if ((a_uVal) & (a_fBitMask)) \
1530 { \
1531 if (!cPrinted++) \
1532 cchOutput += pfnOutput(pvArgOutput, "{" a_szName, sizeof(a_szName)); \
1533 else \
1534 cchOutput += pfnOutput(pvArgOutput, "," a_szName, sizeof(a_szName)); \
1535 (a_uVal) &= ~(a_fBitMask); \
1536 } \
1537 } while (0)
1538#define REG_OUT_CLOSE(a_uVal) \
1539 do { \
1540 if ((a_uVal)) \
1541 { \
1542 cchOutput += pfnOutput(pvArgOutput, !cPrinted ? "{unkn=" : ",unkn=", 6); \
1543 cch = RTStrFormatNumber(&szBuf[0], (a_uVal), 16, 1, -1, fFlags); \
1544 cchOutput += pfnOutput(pvArgOutput, szBuf, cch); \
1545 cPrinted++; \
1546 } \
1547 if (cPrinted) \
1548 cchOutput += pfnOutput(pvArgOutput, "}", 1); \
1549 } while (0)
1550
1551
1552 if (0)
1553 { /* dummy */ }
1554#ifdef STRFORMAT_WITH_X86
1555 /*
1556 * X86 & AMD64.
1557 */
1558 else if ( pszReg - pszArch == 3 + 1
1559 && pszArch[0] == 'x'
1560 && pszArch[1] == '8'
1561 && pszArch[2] == '6')
1562 {
1563 if (REG_EQUALS("cr0"))
1564 {
1565 uint64_t cr0 = va_arg(*pArgs, uint64_t);
1566 fFlags |= RTSTR_F_64BIT;
1567 cch = RTStrFormatNumber(&szBuf[0], cr0, 16, 8, -1, fFlags | RTSTR_F_ZEROPAD);
1568 cchOutput += pfnOutput(pvArgOutput, szBuf, cch);
1569 REG_OUT_BIT(cr0, X86_CR0_PE, "PE");
1570 REG_OUT_BIT(cr0, X86_CR0_MP, "MP");
1571 REG_OUT_BIT(cr0, X86_CR0_EM, "EM");
1572 REG_OUT_BIT(cr0, X86_CR0_TS, "DE");
1573 REG_OUT_BIT(cr0, X86_CR0_ET, "ET");
1574 REG_OUT_BIT(cr0, X86_CR0_NE, "NE");
1575 REG_OUT_BIT(cr0, X86_CR0_WP, "WP");
1576 REG_OUT_BIT(cr0, X86_CR0_AM, "AM");
1577 REG_OUT_BIT(cr0, X86_CR0_NW, "NW");
1578 REG_OUT_BIT(cr0, X86_CR0_CD, "CD");
1579 REG_OUT_BIT(cr0, X86_CR0_PG, "PG");
1580 REG_OUT_CLOSE(cr0);
1581 }
1582 else if (REG_EQUALS("cr4"))
1583 {
1584 uint64_t cr4 = va_arg(*pArgs, uint64_t);
1585 fFlags |= RTSTR_F_64BIT;
1586 cch = RTStrFormatNumber(&szBuf[0], cr4, 16, 8, -1, fFlags | RTSTR_F_ZEROPAD);
1587 cchOutput += pfnOutput(pvArgOutput, szBuf, cch);
1588 REG_OUT_BIT(cr4, X86_CR4_VME, "VME");
1589 REG_OUT_BIT(cr4, X86_CR4_PVI, "PVI");
1590 REG_OUT_BIT(cr4, X86_CR4_TSD, "TSD");
1591 REG_OUT_BIT(cr4, X86_CR4_DE, "DE");
1592 REG_OUT_BIT(cr4, X86_CR4_PSE, "PSE");
1593 REG_OUT_BIT(cr4, X86_CR4_PAE, "PAE");
1594 REG_OUT_BIT(cr4, X86_CR4_MCE, "MCE");
1595 REG_OUT_BIT(cr4, X86_CR4_PGE, "PGE");
1596 REG_OUT_BIT(cr4, X86_CR4_PCE, "PCE");
1597 REG_OUT_BIT(cr4, X86_CR4_OSFXSR, "OSFXSR");
1598 REG_OUT_BIT(cr4, X86_CR4_OSXMMEEXCPT, "OSXMMEEXCPT");
1599 REG_OUT_BIT(cr4, X86_CR4_VMXE, "VMXE");
1600 REG_OUT_BIT(cr4, X86_CR4_SMXE, "SMXE");
1601 REG_OUT_BIT(cr4, X86_CR4_PCIDE, "PCIDE");
1602 REG_OUT_BIT(cr4, X86_CR4_OSXSAVE, "OSXSAVE");
1603 REG_OUT_BIT(cr4, X86_CR4_SMEP, "SMEP");
1604 REG_OUT_BIT(cr4, X86_CR4_SMAP, "SMAP");
1605 REG_OUT_CLOSE(cr4);
1606 }
1607 else
1608 AssertMsgFailed(("Unknown x86 register specified in '%.10s'!\n", pszFormatOrg));
1609 }
1610#endif
1611 else
1612 AssertMsgFailed(("Unknown architecture specified in '%.10s'!\n", pszFormatOrg));
1613#undef REG_OUT_BIT
1614#undef REG_OUT_CLOSE
1615#undef REG_EQUALS
1616 return cchOutput;
1617 }
1618
1619 /*
1620 * Invalid/Unknown. Bitch about it.
1621 */
1622 default:
1623 AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));
1624 break;
1625 }
1626 }
1627 else
1628 AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg));
1629
1630 NOREF(pszFormatOrg);
1631 return 0;
1632}
1633
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette