VirtualBox

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

Last change on this file since 74978 was 74708, checked in by vboxsync, 6 years ago

iprt/string.h: %RMpa - to percent-encode (a)ll reserved characters.

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