1 /* $Id: strformatrt.cpp $ */
3 * IPRT - IPRT String Formatter Extensions.
7 * Copyright (C) 2006-2017 Oracle Corporation
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.
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.
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.
28 /*********************************************************************************************************************************
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 */
37 #include "internal/iprt.h"
40 #include <iprt/assert.h>
41 #include <iprt/string.h>
42 #include <iprt/stdarg.h>
44 # include <iprt/thread.h>
45 # include <iprt/err.h>
47 #include <iprt/ctype.h>
48 #include <iprt/time.h>
50 #include <iprt/path.h>
52 #define STRFORMAT_WITH_X86
53 #ifdef STRFORMAT_WITH_X86
54 # include <iprt/x86.h>
56 #include "internal/string.h"
59 /*********************************************************************************************************************************
61 *********************************************************************************************************************************/
62 static char g_szHexDigits
[17] = "0123456789abcdef";
66 * Helper that formats a 16-bit hex word in a IPv6 address.
68 * @returns Length in chars.
69 * @param pszDst The output buffer. Written from the start.
70 * @param uWord The word to format as hex.
72 static size_t rtstrFormatIPv6HexWord(char *pszDst
, uint16_t uWord
)
77 if (uWord
& UINT16_C(0xff00))
78 cDigits
= uWord
& UINT16_C(0xf000) ? 4 : 3;
80 cDigits
= uWord
& UINT16_C(0x00f0) ? 2 : 1;
85 case 4: pszDst
[off
++] = g_szHexDigits
[(uWord
>> 12) & 0xf]; RT_FALL_THRU();
86 case 3: pszDst
[off
++] = g_szHexDigits
[(uWord
>> 8) & 0xf]; RT_FALL_THRU();
87 case 2: pszDst
[off
++] = g_szHexDigits
[(uWord
>> 4) & 0xf]; RT_FALL_THRU();
88 case 1: pszDst
[off
++] = g_szHexDigits
[(uWord
>> 0) & 0xf];
97 * Helper function to format IPv6 address according to RFC 5952.
99 * @returns The number of bytes formatted.
100 * @param pfnOutput Pointer to output function.
101 * @param pvArgOutput Argument for the output function.
102 * @param pIpv6Addr IPv6 address
104 static size_t rtstrFormatIPv6(PFNRTSTROUTPUT pfnOutput
, void *pvArgOutput
, PCRTNETADDRIPV6 pIpv6Addr
)
106 size_t cch
; /* result */
109 size_t cwLongestZeroRun
;
110 size_t iLongestZeroStart
;
114 Assert(pIpv6Addr
!= NULL
);
117 * Check for embedded IPv4 address.
119 * IPv4-compatible - ::11.22.33.44 (obsolete)
120 * IPv4-mapped - ::ffff:11.22.33.44
121 * IPv4-translated - ::ffff:0:11.22.33.44 (RFC 2765)
123 fEmbeddedIpv4
= false;
124 cwHexPart
= RT_ELEMENTS(pIpv6Addr
->au16
);
125 if ( pIpv6Addr
->au64
[0] == 0
126 && ( ( pIpv6Addr
->au32
[2] == 0
127 && pIpv6Addr
->au32
[3] != 0
128 && pIpv6Addr
->au32
[3] != RT_H2BE_U32_C(1) )
129 || pIpv6Addr
->au32
[2] == RT_H2BE_U32_C(0x0000ffff)
130 || pIpv6Addr
->au32
[2] == RT_H2BE_U32_C(0xffff0000) ) )
132 fEmbeddedIpv4
= true;
137 * Find the longest sequences of two or more zero words.
139 cwLongestZeroRun
= 0;
140 iLongestZeroStart
= 0;
141 for (idx
= 0; idx
< cwHexPart
; idx
++)
142 if (pIpv6Addr
->au16
[idx
] == 0)
144 size_t iZeroStart
= idx
;
148 while (idx
< cwHexPart
&& pIpv6Addr
->au16
[idx
] == 0);
149 cwZeroRun
= idx
- iZeroStart
;
150 if (cwZeroRun
> 1 && cwZeroRun
> cwLongestZeroRun
)
152 cwLongestZeroRun
= cwZeroRun
;
153 iLongestZeroStart
= iZeroStart
;
154 if (cwZeroRun
>= cwHexPart
- idx
)
163 if (cwLongestZeroRun
== 0)
165 for (idx
= 0; idx
< cwHexPart
; ++idx
)
168 cch
+= pfnOutput(pvArgOutput
, ":", 1);
169 cch
+= pfnOutput(pvArgOutput
, szHexWord
, rtstrFormatIPv6HexWord(szHexWord
, RT_BE2H_U16(pIpv6Addr
->au16
[idx
])));
173 cch
+= pfnOutput(pvArgOutput
, ":", 1);
177 const size_t iLongestZeroEnd
= iLongestZeroStart
+ cwLongestZeroRun
;
179 if (iLongestZeroStart
== 0)
180 cch
+= pfnOutput(pvArgOutput
, ":", 1);
182 for (idx
= 0; idx
< iLongestZeroStart
; ++idx
)
184 cch
+= pfnOutput(pvArgOutput
, szHexWord
, rtstrFormatIPv6HexWord(szHexWord
, RT_BE2H_U16(pIpv6Addr
->au16
[idx
])));
185 cch
+= pfnOutput(pvArgOutput
, ":", 1);
188 if (iLongestZeroEnd
== cwHexPart
)
189 cch
+= pfnOutput(pvArgOutput
, ":", 1);
192 for (idx
= iLongestZeroEnd
; idx
< cwHexPart
; ++idx
)
194 cch
+= pfnOutput(pvArgOutput
, ":", 1);
195 cch
+= pfnOutput(pvArgOutput
, szHexWord
, rtstrFormatIPv6HexWord(szHexWord
, RT_BE2H_U16(pIpv6Addr
->au16
[idx
])));
199 cch
+= pfnOutput(pvArgOutput
, ":", 1);
204 cch
+= RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
216 * Callback to format iprt formatting extentions.
217 * See @ref pg_rt_str_format for a reference on the format types.
219 * @returns The number of bytes formatted.
220 * @param pfnOutput Pointer to output function.
221 * @param pvArgOutput Argument for the output function.
222 * @param ppszFormat Pointer to the format string pointer. Advance this till the char
223 * after the format specifier.
224 * @param pArgs Pointer to the argument list. Use this to fetch the arguments.
225 * @param cchWidth Format Width. -1 if not specified.
226 * @param cchPrecision Format Precision. -1 if not specified.
227 * @param fFlags Flags (RTSTR_NTFS_*).
228 * @param chArgSize The argument size specifier, 'l' or 'L'.
230 DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput
, void *pvArgOutput
, const char **ppszFormat
, va_list *pArgs
,
231 int cchWidth
, int cchPrecision
, unsigned fFlags
, char chArgSize
)
233 const char *pszFormatOrg
= *ppszFormat
;
234 char ch
= *(*ppszFormat
)++;
240 ch
= *(*ppszFormat
)++;
257 * Interpret the type.
275 uint8_t cch
; /**< the length of the string. */
276 char sz
[10]; /**< the part following 'R'. */
277 uint8_t cb
; /**< the size of the type. */
278 uint8_t u8Base
; /**< the size of the type. */
279 RTSF enmFormat
; /**< The way to format it. */
280 uint16_t fFlags
; /**< additional RTSTR_F_* flags. */
282 /** Sorted array of types, looked up using binary search! */
285 #define STRMEM(str) sizeof(str) - 1, str
286 { STRMEM("Ci"), sizeof(RTINT
), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
287 { STRMEM("Cp"), sizeof(RTCCPHYS
), 16, RTSF_INTW
, 0 },
288 { STRMEM("Cr"), sizeof(RTCCUINTREG
), 16, RTSF_INTW
, 0 },
289 { STRMEM("Cu"), sizeof(RTUINT
), 10, RTSF_INT
, 0 },
290 { STRMEM("Cv"), sizeof(void *), 16, RTSF_INTW
, 0 },
291 { STRMEM("Cx"), sizeof(RTUINT
), 16, RTSF_INT
, 0 },
292 { STRMEM("Gi"), sizeof(RTGCINT
), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
293 { STRMEM("Gp"), sizeof(RTGCPHYS
), 16, RTSF_INTW
, 0 },
294 { STRMEM("Gr"), sizeof(RTGCUINTREG
), 16, RTSF_INTW
, 0 },
295 { STRMEM("Gu"), sizeof(RTGCUINT
), 10, RTSF_INT
, 0 },
296 { STRMEM("Gv"), sizeof(RTGCPTR
), 16, RTSF_INTW
, 0 },
297 { STRMEM("Gx"), sizeof(RTGCUINT
), 16, RTSF_INT
, 0 },
298 { STRMEM("Hi"), sizeof(RTHCINT
), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
299 { STRMEM("Hp"), sizeof(RTHCPHYS
), 16, RTSF_INTW
, 0 },
300 { STRMEM("Hr"), sizeof(RTHCUINTREG
), 16, RTSF_INTW
, 0 },
301 { STRMEM("Hu"), sizeof(RTHCUINT
), 10, RTSF_INT
, 0 },
302 { STRMEM("Hv"), sizeof(RTHCPTR
), 16, RTSF_INTW
, 0 },
303 { STRMEM("Hx"), sizeof(RTHCUINT
), 16, RTSF_INT
, 0 },
304 { STRMEM("I16"), sizeof(int16_t), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
305 { STRMEM("I32"), sizeof(int32_t), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
306 { STRMEM("I64"), sizeof(int64_t), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
307 { STRMEM("I8"), sizeof(int8_t), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
308 { STRMEM("Kv"), sizeof(RTHCPTR
), 16, RTSF_INT
, RTSTR_F_OBFUSCATE_PTR
},
309 { STRMEM("Rv"), sizeof(RTRCPTR
), 16, RTSF_INTW
, 0 },
310 { STRMEM("Tbool"), sizeof(bool), 10, RTSF_BOOL
, 0 },
311 { STRMEM("Tfile"), sizeof(RTFILE
), 10, RTSF_INT
, 0 },
312 { STRMEM("Tfmode"), sizeof(RTFMODE
), 16, RTSF_INTW
, 0 },
313 { STRMEM("Tfoff"), sizeof(RTFOFF
), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
314 { STRMEM("Tfp16"), sizeof(RTFAR16
), 16, RTSF_FP16
, RTSTR_F_ZEROPAD
},
315 { STRMEM("Tfp32"), sizeof(RTFAR32
), 16, RTSF_FP32
, RTSTR_F_ZEROPAD
},
316 { STRMEM("Tfp64"), sizeof(RTFAR64
), 16, RTSF_FP64
, RTSTR_F_ZEROPAD
},
317 { STRMEM("Tgid"), sizeof(RTGID
), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
318 { STRMEM("Tino"), sizeof(RTINODE
), 16, RTSF_INTW
, 0 },
319 { STRMEM("Tint"), sizeof(RTINT
), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
320 { STRMEM("Tiop"), sizeof(RTIOPORT
), 16, RTSF_INTW
, 0 },
321 { STRMEM("Tldrm"), sizeof(RTLDRMOD
), 16, RTSF_INTW
, 0 },
322 { STRMEM("Tmac"), sizeof(PCRTMAC
), 16, RTSF_MAC
, 0 },
323 { STRMEM("Tnaddr"), sizeof(PCRTNETADDR
), 10, RTSF_NETADDR
,0 },
324 { STRMEM("Tnaipv4"), sizeof(RTNETADDRIPV4
), 10, RTSF_IPV4
, 0 },
325 { STRMEM("Tnaipv6"), sizeof(PCRTNETADDRIPV6
),16, RTSF_IPV6
, 0 },
326 { STRMEM("Tnthrd"), sizeof(RTNATIVETHREAD
), 16, RTSF_INTW
, 0 },
327 { STRMEM("Tproc"), sizeof(RTPROCESS
), 16, RTSF_INTW
, 0 },
328 { STRMEM("Tptr"), sizeof(RTUINTPTR
), 16, RTSF_INTW
, 0 },
329 { STRMEM("Treg"), sizeof(RTCCUINTREG
), 16, RTSF_INTW
, 0 },
330 { STRMEM("Tsel"), sizeof(RTSEL
), 16, RTSF_INTW
, 0 },
331 { STRMEM("Tsem"), sizeof(RTSEMEVENT
), 16, RTSF_INTW
, 0 },
332 { STRMEM("Tsock"), sizeof(RTSOCKET
), 10, RTSF_INT
, 0 },
333 { STRMEM("Tthrd"), sizeof(RTTHREAD
), 16, RTSF_INTW
, 0 },
334 { STRMEM("Tuid"), sizeof(RTUID
), 10, RTSF_INT
, RTSTR_F_VALSIGNED
},
335 { STRMEM("Tuint"), sizeof(RTUINT
), 10, RTSF_INT
, 0 },
336 { STRMEM("Tunicp"), sizeof(RTUNICP
), 16, RTSF_INTW
, RTSTR_F_ZEROPAD
},
337 { STRMEM("Tutf16"), sizeof(RTUTF16
), 16, RTSF_INTW
, RTSTR_F_ZEROPAD
},
338 { STRMEM("Tuuid"), sizeof(PCRTUUID
), 16, RTSF_UUID
, 0 },
339 { STRMEM("Txint"), sizeof(RTUINT
), 16, RTSF_INT
, 0 },
340 { STRMEM("U16"), sizeof(uint16_t), 10, RTSF_INT
, 0 },
341 { STRMEM("U32"), sizeof(uint32_t), 10, RTSF_INT
, 0 },
342 { STRMEM("U64"), sizeof(uint64_t), 10, RTSF_INT
, 0 },
343 { STRMEM("U8"), sizeof(uint8_t), 10, RTSF_INT
, 0 },
344 { STRMEM("X16"), sizeof(uint16_t), 16, RTSF_INT
, 0 },
345 { STRMEM("X32"), sizeof(uint32_t), 16, RTSF_INT
, 0 },
346 { STRMEM("X64"), sizeof(uint64_t), 16, RTSF_INT
, 0 },
347 { STRMEM("X8"), sizeof(uint8_t), 16, RTSF_INT
, 0 },
350 static const char s_szNull
[] = "<NULL>";
352 const char *pszType
= *ppszFormat
- 1;
354 int iEnd
= RT_ELEMENTS(s_aTypes
) - 1;
355 int i
= RT_ELEMENTS(s_aTypes
) / 2;
373 RTNETADDRIPV4 Ipv4Addr
;
374 PCRTNETADDRIPV6 pIpv6Addr
;
375 PCRTNETADDR pNetAddr
;
379 AssertMsg(!chArgSize
, ("Not argument size '%c' for RT types! '%.10s'\n", chArgSize
, pszFormatOrg
));
380 RT_NOREF_PV(chArgSize
);
383 * Lookup the type - binary search.
387 int iDiff
= strncmp(pszType
, s_aTypes
[i
].sz
, s_aTypes
[i
].cch
);
392 AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg
));
401 AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg
));
404 i
= iStart
+ (iEnd
- iStart
) / 2;
408 * Advance the format string and merge flags.
410 *ppszFormat
+= s_aTypes
[i
].cch
- 1;
411 fFlags
|= s_aTypes
[i
].fFlags
;
414 * Fetch the argument.
415 * It's important that a signed value gets sign-extended up to 64-bit.
418 if (fFlags
& RTSTR_F_VALSIGNED
)
420 switch (s_aTypes
[i
].cb
)
423 u
.i64
= va_arg(*pArgs
, /*int8_t*/int);
424 fFlags
|= RTSTR_F_8BIT
;
426 case sizeof(int16_t):
427 u
.i64
= va_arg(*pArgs
, /*int16_t*/int);
428 fFlags
|= RTSTR_F_16BIT
;
430 case sizeof(int32_t):
431 u
.i64
= va_arg(*pArgs
, int32_t);
432 fFlags
|= RTSTR_F_32BIT
;
434 case sizeof(int64_t):
435 u
.i64
= va_arg(*pArgs
, int64_t);
436 fFlags
|= RTSTR_F_64BIT
;
439 AssertMsgFailed(("Invalid format error, size %d'!\n", s_aTypes
[i
].cb
));
445 switch (s_aTypes
[i
].cb
)
447 case sizeof(uint8_t):
448 u
.u8
= va_arg(*pArgs
, /*uint8_t*/unsigned);
449 fFlags
|= RTSTR_F_8BIT
;
451 case sizeof(uint16_t):
452 u
.u16
= va_arg(*pArgs
, /*uint16_t*/unsigned);
453 fFlags
|= RTSTR_F_16BIT
;
455 case sizeof(uint32_t):
456 u
.u32
= va_arg(*pArgs
, uint32_t);
457 fFlags
|= RTSTR_F_32BIT
;
459 case sizeof(uint64_t):
460 u
.u64
= va_arg(*pArgs
, uint64_t);
461 fFlags
|= RTSTR_F_64BIT
;
463 case sizeof(RTFAR32
):
464 u
.fp32
= va_arg(*pArgs
, RTFAR32
);
466 case sizeof(RTFAR64
):
467 u
.fp64
= va_arg(*pArgs
, RTFAR64
);
470 AssertMsgFailed(("Invalid format error, size %d'!\n", s_aTypes
[i
].cb
));
477 * For now don't show the address.
479 if (fFlags
& RTSTR_F_OBFUSCATE_PTR
)
481 cch
= rtStrFormatKernelAddress(szBuf
, sizeof(szBuf
), u
.uR0Ptr
, cchWidth
, cchPrecision
, fFlags
);
482 return pfnOutput(pvArgOutput
, szBuf
, cch
);
489 switch (s_aTypes
[i
].enmFormat
)
493 cch
= RTStrFormatNumber(szBuf
, u
.u64
, s_aTypes
[i
].u8Base
, cchWidth
, cchPrecision
, fFlags
);
497 /* hex which defaults to max width. */
500 Assert(s_aTypes
[i
].u8Base
== 16);
503 cchWidth
= s_aTypes
[i
].cb
* 2 + (fFlags
& RTSTR_F_SPECIAL
? 2 : 0);
504 fFlags
|= RTSTR_F_ZEROPAD
;
506 cch
= RTStrFormatNumber(szBuf
, u
.u64
, s_aTypes
[i
].u8Base
, cchWidth
, cchPrecision
, fFlags
);
512 static const char s_szTrue
[] = "true ";
513 static const char s_szFalse
[] = "false";
515 return pfnOutput(pvArgOutput
, s_szTrue
, sizeof(s_szTrue
) - 1);
517 return pfnOutput(pvArgOutput
, s_szFalse
, sizeof(s_szFalse
) - 1);
518 /* invalid boolean value */
519 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0, "!%lld!", u
.u64
);
524 fFlags
&= ~(RTSTR_F_VALSIGNED
| RTSTR_F_BIT_MASK
| RTSTR_F_WIDTH
| RTSTR_F_PRECISION
| RTSTR_F_THOUSAND_SEP
);
525 cch
= RTStrFormatNumber(&szBuf
[0], u
.fp16
.sel
, 16, 4, -1, fFlags
| RTSTR_F_16BIT
);
528 cch
= RTStrFormatNumber(&szBuf
[5], u
.fp16
.off
, 16, 4, -1, fFlags
| RTSTR_F_16BIT
);
535 fFlags
&= ~(RTSTR_F_VALSIGNED
| RTSTR_F_BIT_MASK
| RTSTR_F_WIDTH
| RTSTR_F_PRECISION
| RTSTR_F_THOUSAND_SEP
);
536 cch
= RTStrFormatNumber(&szBuf
[0], u
.fp32
.sel
, 16, 4, -1, fFlags
| RTSTR_F_16BIT
);
539 cch
= RTStrFormatNumber(&szBuf
[5], u
.fp32
.off
, 16, 8, -1, fFlags
| RTSTR_F_32BIT
);
546 fFlags
&= ~(RTSTR_F_VALSIGNED
| RTSTR_F_BIT_MASK
| RTSTR_F_WIDTH
| RTSTR_F_PRECISION
| RTSTR_F_THOUSAND_SEP
);
547 cch
= RTStrFormatNumber(&szBuf
[0], u
.fp64
.sel
, 16, 4, -1, fFlags
| RTSTR_F_16BIT
);
550 cch
= RTStrFormatNumber(&szBuf
[5], u
.fp64
.off
, 16, 16, -1, fFlags
| RTSTR_F_64BIT
);
557 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
566 if (VALID_PTR(u
.pIpv6Addr
))
567 return rtstrFormatIPv6(pfnOutput
, pvArgOutput
, u
.pIpv6Addr
);
568 return pfnOutput(pvArgOutput
, s_szNull
, sizeof(s_szNull
) - 1);
573 if (VALID_PTR(u
.pMac
))
574 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
575 "%02x:%02x:%02x:%02x:%02x:%02x",
582 return pfnOutput(pvArgOutput
, s_szNull
, sizeof(s_szNull
) - 1);
587 if (VALID_PTR(u
.pNetAddr
))
589 switch (u
.pNetAddr
->enmType
)
591 case RTNETADDRTYPE_IPV4
:
592 if (u
.pNetAddr
->uPort
== RTNETADDR_PORT_NA
)
593 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
595 u
.pNetAddr
->uAddr
.IPv4
.au8
[0],
596 u
.pNetAddr
->uAddr
.IPv4
.au8
[1],
597 u
.pNetAddr
->uAddr
.IPv4
.au8
[2],
598 u
.pNetAddr
->uAddr
.IPv4
.au8
[3]);
599 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
601 u
.pNetAddr
->uAddr
.IPv4
.au8
[0],
602 u
.pNetAddr
->uAddr
.IPv4
.au8
[1],
603 u
.pNetAddr
->uAddr
.IPv4
.au8
[2],
604 u
.pNetAddr
->uAddr
.IPv4
.au8
[3],
607 case RTNETADDRTYPE_IPV6
:
608 if (u
.pNetAddr
->uPort
== RTNETADDR_PORT_NA
)
609 return rtstrFormatIPv6(pfnOutput
, pvArgOutput
, &u
.pNetAddr
->uAddr
.IPv6
);
611 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
613 &u
.pNetAddr
->uAddr
.IPv6
,
616 case RTNETADDRTYPE_MAC
:
617 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
618 "%02x:%02x:%02x:%02x:%02x:%02x",
619 u
.pNetAddr
->uAddr
.Mac
.au8
[0],
620 u
.pNetAddr
->uAddr
.Mac
.au8
[1],
621 u
.pNetAddr
->uAddr
.Mac
.au8
[2],
622 u
.pNetAddr
->uAddr
.Mac
.au8
[3],
623 u
.pNetAddr
->uAddr
.Mac
.au8
[4],
624 u
.pNetAddr
->uAddr
.Mac
.au8
[5]);
627 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
628 "unsupported-netaddr-type=%u", u
.pNetAddr
->enmType
);
632 return pfnOutput(pvArgOutput
, s_szNull
, sizeof(s_szNull
) - 1);
637 if (VALID_PTR(u
.pUuid
))
639 /* cannot call RTUuidToStr because of GC/R0. */
640 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
641 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
642 RT_H2LE_U32(u
.pUuid
->Gen
.u32TimeLow
),
643 RT_H2LE_U16(u
.pUuid
->Gen
.u16TimeMid
),
644 RT_H2LE_U16(u
.pUuid
->Gen
.u16TimeHiAndVersion
),
645 u
.pUuid
->Gen
.u8ClockSeqHiAndReserved
,
646 u
.pUuid
->Gen
.u8ClockSeqLow
,
647 u
.pUuid
->Gen
.au8Node
[0],
648 u
.pUuid
->Gen
.au8Node
[1],
649 u
.pUuid
->Gen
.au8Node
[2],
650 u
.pUuid
->Gen
.au8Node
[3],
651 u
.pUuid
->Gen
.au8Node
[4],
652 u
.pUuid
->Gen
.au8Node
[5]);
654 return pfnOutput(pvArgOutput
, s_szNull
, sizeof(s_szNull
) - 1);
658 AssertMsgFailed(("Internal error %d\n", s_aTypes
[i
].enmFormat
));
663 * Finally, output the formatted string and return.
665 return pfnOutput(pvArgOutput
, szBuf
, cch
);
672 * Base name printing, big endian UTF-16.
676 switch (*(*ppszFormat
)++)
680 const char *pszLastSep
;
681 const char *psz
= pszLastSep
= va_arg(*pArgs
, const char *);
683 return pfnOutput(pvArgOutput
, RT_STR_TUPLE("<null>"));
685 while ((ch
= *psz
) != '\0')
687 if (RTPATH_IS_SEP(ch
))
691 while ((ch
= *psz
) != '\0' && RTPATH_IS_SEP(ch
));
699 return pfnOutput(pvArgOutput
, pszLastSep
, psz
- pszLastSep
);
704 if (chArgSize
== 'l')
706 /* utf-16BE -> utf-8 */
708 PCRTUTF16 pwszStr
= va_arg(*pArgs
, PRTUTF16
);
710 if (RT_VALID_PTR(pwszStr
))
713 while (cchStr
< cchPrecision
&& pwszStr
[cchStr
] != '\0')
718 static RTUTF16 s_wszBigNull
[] =
720 RT_H2BE_U16_C((uint16_t)'<'), RT_H2BE_U16_C((uint16_t)'N'), RT_H2BE_U16_C((uint16_t)'U'),
721 RT_H2BE_U16_C((uint16_t)'L'), RT_H2BE_U16_C((uint16_t)'L'), RT_H2BE_U16_C((uint16_t)'>'), '\0'
723 pwszStr
= s_wszBigNull
;
724 cchStr
= RT_ELEMENTS(s_wszBigNull
) - 1;
728 if (!(fFlags
& RTSTR_F_LEFT
))
729 while (--cchWidth
>= cchStr
)
730 cch
+= pfnOutput(pvArgOutput
, " ", 1);
734 /** @todo \#ifndef IN_RC*/
737 RTUtf16BigGetCpEx(&pwszStr
, &Cp
);
738 char *pszEnd
= RTStrPutCp(szBuf
, Cp
);
740 cch
+= pfnOutput(pvArgOutput
, szBuf
, pszEnd
- szBuf
);
742 szBuf
[0] = (char)(*pwszStr
++ >> 8);
743 cch
+= pfnOutput(pvArgOutput
, szBuf
, 1);
746 while (--cchWidth
>= 0)
747 cch
+= pfnOutput(pvArgOutput
, " ", 1);
753 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg
));
761 * Pretty function / method name printing.
765 switch (*(*ppszFormat
)++)
768 * Pretty function / method name printing.
769 * This isn't 100% right (see classic signal prototype) and it assumes
770 * standardized names, but it'll do for today.
774 const char *pszStart
;
775 const char *psz
= pszStart
= va_arg(*pArgs
, const char *);
779 return pfnOutput(pvArgOutput
, RT_STR_TUPLE("<null>"));
781 while ((ch
= *psz
) != '\0' && ch
!= '(')
783 if (RT_C_IS_BLANK(ch
))
786 while ((ch
= *psz
) != '\0' && (RT_C_IS_BLANK(ch
) || ch
== '('))
788 if (ch
&& cAngle
== 0)
807 return pfnOutput(pvArgOutput
, pszStart
, psz
- pszStart
);
811 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg
));
819 * hex dumping and COM/XPCOM.
823 switch (*(*ppszFormat
)++)
830 uint8_t *pu8
= va_arg(*pArgs
, uint8_t *);
831 if (cchPrecision
< 0)
835 switch (*(*ppszFormat
)++)
848 while (off
< cchPrecision
)
851 cch
+= RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
852 "%s%0*p %04x:", off
? "\n" : "", sizeof(pu8
) * 2, (uintptr_t)pu8
, off
);
853 for (i
= 0; i
< cchWidth
&& off
+ i
< cchPrecision
; i
++)
854 cch
+= RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
855 off
+ i
< cchPrecision
? !(i
& 7) && i
? "-%02x" : " %02x" : " ",
857 while (i
++ < cchWidth
)
858 cch
+= pfnOutput(pvArgOutput
, " ", 3);
860 cch
+= pfnOutput(pvArgOutput
, " ", 1);
862 for (i
= 0; i
< cchWidth
&& off
+ i
< cchPrecision
; i
++)
865 cch
+= pfnOutput(pvArgOutput
, u8
< 127 && u8
>= 32 ? (const char *)&u8
: ".", 1);
876 * Regular hex dump with dittoing.
887 offEndDupCheck
= cchPrecision
- cchWidth
;
889 while (off
< cchPrecision
)
892 if ( off
>= offEndDupCheck
894 || memcmp(pu8
, pu8
- cchWidth
, cchWidth
) != 0
895 || ( cDuplicates
== 0
896 && ( off
+ cchWidth
>= offEndDupCheck
897 || memcmp(pu8
+ cchWidth
, pu8
, cchWidth
) != 0)) )
901 cch
+= RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
902 "\n%.*s **** <ditto x %u>",
903 sizeof(pu8
) * 2, "****************", cDuplicates
);
907 cch
+= RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
908 "%s%0*p %04x:", off
? "\n" : "", sizeof(pu8
) * 2, (uintptr_t)pu8
, off
);
909 for (i
= 0; i
< cchWidth
&& off
+ i
< cchPrecision
; i
++)
910 cch
+= RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0,
911 off
+ i
< cchPrecision
? !(i
& 7) && i
912 ? "-%02x" : " %02x" : " ",
914 while (i
++ < cchWidth
)
915 cch
+= pfnOutput(pvArgOutput
, " ", 3);
917 cch
+= pfnOutput(pvArgOutput
, " ", 1);
919 for (i
= 0; i
< cchWidth
&& off
+ i
< cchPrecision
; i
++)
922 cch
+= pfnOutput(pvArgOutput
, u8
< 127 && u8
>= 32 ? (const char *)&u8
: ".", 1);
940 if (cchPrecision
-- > 0)
942 cch
= RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0, "%02x", *pu8
++);
943 for (; cchPrecision
> 0; cchPrecision
--, pu8
++)
944 cch
+= RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0, " %02x", *pu8
);
951 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg
));
956 return pfnOutput(pvArgOutput
, RT_STR_TUPLE("<null>"));
963 * XPCOM / COM status code: %Rhrc, %Rhrf, %Rhra
964 * ASSUMES: If Windows Then COM else XPCOM.
968 uint32_t hrc
= va_arg(*pArgs
, uint32_t);
969 PCRTCOMERRMSG pMsg
= RTErrCOMGet(hrc
);
970 switch (*(*ppszFormat
)++)
973 return pfnOutput(pvArgOutput
, pMsg
->pszDefine
, strlen(pMsg
->pszDefine
));
975 return pfnOutput(pvArgOutput
, pMsg
->pszMsgFull
,strlen(pMsg
->pszMsgFull
));
977 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0, "%s (0x%08X) - %s", pMsg
->pszDefine
, hrc
, pMsg
->pszMsgFull
);
979 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg
));
984 #endif /* IN_RING3 */
987 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg
));
995 * iprt status code: %Rrc, %Rrs, %Rrf, %Rra.
999 int rc
= va_arg(*pArgs
, int);
1000 #ifdef IN_RING3 /* we don't want this anywhere else yet. */
1001 PCRTSTATUSMSG pMsg
= RTErrGet(rc
);
1002 switch (*(*ppszFormat
)++)
1005 return pfnOutput(pvArgOutput
, pMsg
->pszDefine
, strlen(pMsg
->pszDefine
));
1007 return pfnOutput(pvArgOutput
, pMsg
->pszMsgShort
, strlen(pMsg
->pszMsgShort
));
1009 return pfnOutput(pvArgOutput
, pMsg
->pszMsgFull
, strlen(pMsg
->pszMsgFull
));
1011 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0, "%s (%d) - %s", pMsg
->pszDefine
, rc
, pMsg
->pszMsgFull
);
1013 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg
));
1016 #else /* !IN_RING3 */
1017 switch (*(*ppszFormat
)++)
1023 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0, "%d", rc
);
1025 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg
));
1028 #endif /* !IN_RING3 */
1032 #if defined(IN_RING3)
1034 * Windows status code: %Rwc, %Rwf, %Rwa
1038 long rc
= va_arg(*pArgs
, long);
1039 # if defined(RT_OS_WINDOWS)
1040 PCRTWINERRMSG pMsg
= RTErrWinGet(rc
);
1042 switch (*(*ppszFormat
)++)
1044 # if defined(RT_OS_WINDOWS)
1046 return pfnOutput(pvArgOutput
, pMsg
->pszDefine
, strlen(pMsg
->pszDefine
));
1048 return pfnOutput(pvArgOutput
, pMsg
->pszMsgFull
,strlen(pMsg
->pszMsgFull
));
1050 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0, "%s (0x%08X) - %s", pMsg
->pszDefine
, rc
, pMsg
->pszMsgFull
);
1055 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, 0, "0x%08X", rc
);
1058 AssertMsgFailed(("Invalid status code format type '%.10s'!\n", pszFormatOrg
));
1063 #endif /* IN_RING3 */
1066 * Group 4, structure dumpers.
1071 * Interpret the type.
1077 /** Set if it's a pointer */
1078 #define RTST_FLAGS_POINTER RT_BIT(0)
1081 uint8_t cch
; /**< the length of the string. */
1082 char sz
[16-2]; /**< the part following 'R'. */
1083 uint8_t cb
; /**< the size of the argument. */
1084 uint8_t fFlags
; /**< RTST_FLAGS_* */
1085 RTST enmType
; /**< The structure type. */
1087 /** Sorted array of types, looked up using binary search! */
1090 #define STRMEM(str) sizeof(str) - 1, str
1091 { STRMEM("Dtimespec"), sizeof(PCRTTIMESPEC
), RTST_FLAGS_POINTER
, RTST_TIMESPEC
},
1094 const char *pszType
= *ppszFormat
- 1;
1096 int iEnd
= RT_ELEMENTS(s_aTypes
) - 1;
1097 int i
= RT_ELEMENTS(s_aTypes
) / 2;
1103 PCRTTIMESPEC pTimeSpec
;
1106 AssertMsg(!chArgSize
, ("Not argument size '%c' for RT types! '%.10s'\n", chArgSize
, pszFormatOrg
));
1109 * Lookup the type - binary search.
1113 int iDiff
= strncmp(pszType
, s_aTypes
[i
].sz
, s_aTypes
[i
].cch
);
1118 AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg
));
1127 AssertMsgFailed(("Invalid format type '%.10s'!\n", pszFormatOrg
));
1130 i
= iStart
+ (iEnd
- iStart
) / 2;
1132 *ppszFormat
+= s_aTypes
[i
].cch
- 1;
1135 * Fetch the argument.
1138 switch (s_aTypes
[i
].cb
)
1140 case sizeof(const void *):
1141 u
.pv
= va_arg(*pArgs
, const void *);
1144 AssertMsgFailed(("Invalid format error, size %d'!\n", s_aTypes
[i
].cb
));
1149 * If it's a pointer, we'll check if it's valid before going on.
1151 if ((s_aTypes
[i
].fFlags
& RTST_FLAGS_POINTER
) && !VALID_PTR(u
.pv
))
1152 return pfnOutput(pvArgOutput
, RT_STR_TUPLE("<null>"));
1155 * Format the output.
1157 switch (s_aTypes
[i
].enmType
)
1160 return RTStrFormat(pfnOutput
, pvArgOutput
, NULL
, NULL
, "%'lld ns", RTTimeSpecGetNano(u
.pTimeSpec
));
1163 AssertMsgFailed(("Invalid/unhandled enmType=%d\n", s_aTypes
[i
].enmType
));
1171 * Group 5, XML / HTML escapers.
1175 char chWhat
= (*ppszFormat
)[0];
1176 bool fAttr
= chWhat
== 'a';
1177 char chType
= (*ppszFormat
)[1];
1178 AssertMsgBreak(chWhat
== 'a' || chWhat
== 'e', ("Invalid IPRT format type '%.10s'!\n", pszFormatOrg
));
1184 static const char s_szElemEscape
[] = "<>&\"'";
1185 static const char s_szAttrEscape
[] = "<>&\"\n\r"; /* more? */
1186 const char * const pszEscape
= fAttr
? s_szAttrEscape
: s_szElemEscape
;
1187 size_t const cchEscape
= (fAttr
? RT_ELEMENTS(s_szAttrEscape
) : RT_ELEMENTS(s_szElemEscape
)) - 1;
1188 size_t cchOutput
= 0;
1189 const char *pszStr
= va_arg(*pArgs
, char *);
1194 if (!VALID_PTR(pszStr
))
1196 cchStr
= RTStrNLen(pszStr
, (unsigned)cchPrecision
);
1199 cchOutput
+= pfnOutput(pvArgOutput
, "\"", 1);
1200 if (!(fFlags
& RTSTR_F_LEFT
))
1201 while (--cchWidth
>= cchStr
)
1202 cchOutput
+= pfnOutput(pvArgOutput
, " ", 1);
1204 offLast
= offCur
= 0;
1205 while (offCur
< cchStr
)
1207 if (memchr(pszEscape
, pszStr
[offCur
], cchEscape
))
1209 if (offLast
< offCur
)
1210 cchOutput
+= pfnOutput(pvArgOutput
, &pszStr
[offLast
], offCur
- offLast
);
1211 switch (pszStr
[offCur
])
1213 case '<': cchOutput
+= pfnOutput(pvArgOutput
, "<", 4); break;
1214 case '>': cchOutput
+= pfnOutput(pvArgOutput
, ">", 4); break;
1215 case '&': cchOutput
+= pfnOutput(pvArgOutput
, "&", 5); break;
1216 case '\'': cchOutput
+= pfnOutput(pvArgOutput
, "'", 6); break;
1217 case '"': cchOutput
+= pfnOutput(pvArgOutput
, """, 6); break;
1218 case '\n': cchOutput
+= pfnOutput(pvArgOutput
, "
", 5); break;
1219 case '\r': cchOutput
+= pfnOutput(pvArgOutput
, "
", 5); break;
1223 offLast
= offCur
+ 1;
1227 if (offLast
< offCur
)
1228 cchOutput
+= pfnOutput(pvArgOutput
, &pszStr
[offLast
], offCur
- offLast
);
1230 while (--cchWidth
>= cchStr
)
1231 cchOutput
+= pfnOutput(pvArgOutput
, " ", 1);
1233 cchOutput
+= pfnOutput(pvArgOutput
, "\"", 1);
1238 AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg
));
1242 #endif /* IN_RING3 */
1246 * Groups 6 - CPU Architecture Register Formatters.
1251 char const * const pszArch
= *ppszFormat
;
1252 const char *pszReg
= pszArch
;
1253 size_t cchOutput
= 0;
1258 while ((ch
= *pszReg
++) && ch
!= '[')
1260 AssertMsgBreak(ch
== '[', ("Malformed IPRT architecture register format type '%.10s'!\n", pszFormatOrg
));
1263 while ((ch
= pszReg
[cchReg
]) && ch
!= ']')
1265 AssertMsgBreak(ch
== ']', ("Malformed IPRT architecture register format type '%.10s'!\n", pszFormatOrg
));
1267 *ppszFormat
= &pszReg
[cchReg
+ 1];
1270 #define REG_EQUALS(a_szReg) (sizeof(a_szReg) - 1 == cchReg && !strncmp(a_szReg, pszReg, sizeof(a_szReg) - 1))
1271 #define REG_OUT_BIT(a_uVal, a_fBitMask, a_szName) \
1273 if ((a_uVal) & (a_fBitMask)) \
1276 cchOutput += pfnOutput(pvArgOutput, "{" a_szName, sizeof(a_szName)); \
1278 cchOutput += pfnOutput(pvArgOutput, "," a_szName, sizeof(a_szName)); \
1279 (a_uVal) &= ~(a_fBitMask); \
1282 #define REG_OUT_CLOSE(a_uVal) \
1286 cchOutput += pfnOutput(pvArgOutput, !cPrinted ? "{unkn=" : ",unkn=", 6); \
1287 cch = RTStrFormatNumber(&szBuf[0], (a_uVal), 16, 1, -1, fFlags); \
1288 cchOutput += pfnOutput(pvArgOutput, szBuf, cch); \
1292 cchOutput += pfnOutput(pvArgOutput, "}", 1); \
1298 #ifdef STRFORMAT_WITH_X86
1302 else if ( pszReg
- pszArch
== 3 + 1
1303 && pszArch
[0] == 'x'
1304 && pszArch
[1] == '8'
1305 && pszArch
[2] == '6')
1307 if (REG_EQUALS("cr0"))
1309 uint64_t cr0
= va_arg(*pArgs
, uint64_t);
1310 fFlags
|= RTSTR_F_64BIT
;
1311 cch
= RTStrFormatNumber(&szBuf
[0], cr0
, 16, 8, -1, fFlags
| RTSTR_F_ZEROPAD
);
1312 cchOutput
+= pfnOutput(pvArgOutput
, szBuf
, cch
);
1313 REG_OUT_BIT(cr0
, X86_CR0_PE
, "PE");
1314 REG_OUT_BIT(cr0
, X86_CR0_MP
, "MP");
1315 REG_OUT_BIT(cr0
, X86_CR0_EM
, "EM");
1316 REG_OUT_BIT(cr0
, X86_CR0_TS
, "DE");
1317 REG_OUT_BIT(cr0
, X86_CR0_ET
, "ET");
1318 REG_OUT_BIT(cr0
, X86_CR0_NE
, "NE");
1319 REG_OUT_BIT(cr0
, X86_CR0_WP
, "WP");
1320 REG_OUT_BIT(cr0
, X86_CR0_AM
, "AM");
1321 REG_OUT_BIT(cr0
, X86_CR0_NW
, "NW");
1322 REG_OUT_BIT(cr0
, X86_CR0_CD
, "CD");
1323 REG_OUT_BIT(cr0
, X86_CR0_PG
, "PG");
1326 else if (REG_EQUALS("cr4"))
1328 uint64_t cr4
= va_arg(*pArgs
, uint64_t);
1329 fFlags
|= RTSTR_F_64BIT
;
1330 cch
= RTStrFormatNumber(&szBuf
[0], cr4
, 16, 8, -1, fFlags
| RTSTR_F_ZEROPAD
);
1331 cchOutput
+= pfnOutput(pvArgOutput
, szBuf
, cch
);
1332 REG_OUT_BIT(cr4
, X86_CR4_VME
, "VME");
1333 REG_OUT_BIT(cr4
, X86_CR4_PVI
, "PVI");
1334 REG_OUT_BIT(cr4
, X86_CR4_TSD
, "TSD");
1335 REG_OUT_BIT(cr4
, X86_CR4_DE
, "DE");
1336 REG_OUT_BIT(cr4
, X86_CR4_PSE
, "PSE");
1337 REG_OUT_BIT(cr4
, X86_CR4_PAE
, "PAE");
1338 REG_OUT_BIT(cr4
, X86_CR4_MCE
, "MCE");
1339 REG_OUT_BIT(cr4
, X86_CR4_PGE
, "PGE");
1340 REG_OUT_BIT(cr4
, X86_CR4_PCE
, "PCE");
1341 REG_OUT_BIT(cr4
, X86_CR4_OSFXSR
, "OSFXSR");
1342 REG_OUT_BIT(cr4
, X86_CR4_OSXMMEEXCPT
, "OSXMMEEXCPT");
1343 REG_OUT_BIT(cr4
, X86_CR4_VMXE
, "VMXE");
1344 REG_OUT_BIT(cr4
, X86_CR4_SMXE
, "SMXE");
1345 REG_OUT_BIT(cr4
, X86_CR4_PCIDE
, "PCIDE");
1346 REG_OUT_BIT(cr4
, X86_CR4_OSXSAVE
, "OSXSAVE");
1347 REG_OUT_BIT(cr4
, X86_CR4_SMEP
, "SMEP");
1348 REG_OUT_BIT(cr4
, X86_CR4_SMAP
, "SMAP");
1352 AssertMsgFailed(("Unknown x86 register specified in '%.10s'!\n", pszFormatOrg
));
1356 AssertMsgFailed(("Unknown architecture specified in '%.10s'!\n", pszFormatOrg
));
1358 #undef REG_OUT_CLOSE
1364 * Invalid/Unknown. Bitch about it.
1367 AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg
));
1372 AssertMsgFailed(("Invalid IPRT format type '%.10s'!\n", pszFormatOrg
));
1374 NOREF(pszFormatOrg
);