VirtualBox

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

Last change on this file since 80565 was 80565, checked in by vboxsync, 5 years ago

IPRT/string.h: Added hexformatting variants that takes an additional 64-bit offset/address argument to be used instead of the memory pointer: %RhXd, %RhXD, %RhXs Also changed space between the pointer and offset to a slash.

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

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