VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/string/strformat.cpp

Last change on this file was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 30.5 KB
Line 
1/* $Id: strformat.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - String Formatter.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_STRING
42#include <iprt/string.h>
43#include "internal/iprt.h"
44
45#include <iprt/assert.h>
46#ifdef IN_RING3
47# include <iprt/alloc.h>
48# include <iprt/errcore.h>
49# include <iprt/uni.h>
50# include <iprt/utf16.h>
51#endif
52#include <iprt/ctype.h>
53#include <iprt/string.h>
54#include <iprt/stdarg.h>
55#include "internal/string.h"
56
57
58/**
59 * Deals with bad pointers.
60 */
61DECLHIDDEN(size_t) rtStrFormatBadPointer(size_t cch, PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, int cchWidth,
62 unsigned fFlags, void const *pvStr, char szTmp[64], const char *pszTag, int cchTag)
63{
64 static char const s_szNull[] = "<NULL>";
65 int cchStr = !pvStr ? sizeof(s_szNull) - 1 : 1 + sizeof(void *) * 2 + cchTag + 1;
66
67 if (!(fFlags & RTSTR_F_LEFT))
68 while (--cchWidth >= cchStr)
69 cch += pfnOutput(pvArgOutput, " ", 1);
70
71 cchWidth -= cchStr;
72 if (!pvStr)
73 cch += pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1);
74 else
75 {
76 cch += pfnOutput(pvArgOutput, "<", 1);
77 cchStr = RTStrFormatNumber(&szTmp[0], (uintptr_t)pvStr, 16, sizeof(char *) * 2, 0, RTSTR_F_ZEROPAD);
78 cch += pfnOutput(pvArgOutput, szTmp, cchStr);
79 cch += pfnOutput(pvArgOutput, pszTag, cchTag);
80 cch += pfnOutput(pvArgOutput, ">", 1);
81 }
82
83 while (--cchWidth >= 0)
84 cch += pfnOutput(pvArgOutput, " ", 1);
85 return cch;
86}
87
88
89/**
90 * Finds the length of a string up to cchMax.
91 * @returns Length.
92 * @param psz Pointer to string.
93 * @param cchMax Max length.
94 */
95static unsigned rtStrFormatStrNLen(const char *psz, unsigned cchMax)
96{
97 const char *pszC = psz;
98
99 while (cchMax-- > 0 && *psz != '\0')
100 psz++;
101
102 return (unsigned)(psz - pszC);
103}
104
105
106/**
107 * Finds the length of a string up to cchMax.
108 * @returns Length.
109 * @param pwsz Pointer to string.
110 * @param cchMax Max length.
111 */
112static unsigned rtStrFormatStrNLenUtf16(PCRTUTF16 pwsz, unsigned cchMax)
113{
114#ifdef IN_RING3
115 unsigned cwc = 0;
116 while (cchMax-- > 0)
117 {
118 RTUNICP cp;
119 int rc = RTUtf16GetCpEx(&pwsz, &cp);
120 AssertRC(rc);
121 if (RT_FAILURE(rc) || !cp)
122 break;
123 cwc++;
124 }
125 return cwc;
126#else /* !IN_RING3 */
127 PCRTUTF16 pwszC = pwsz;
128
129 while (cchMax-- > 0 && *pwsz != '\0')
130 pwsz++;
131
132 return (unsigned)(pwsz - pwszC);
133#endif /* !IN_RING3 */
134}
135
136
137/**
138 * Finds the length of a string up to cchMax.
139 * @returns Length.
140 * @param pusz Pointer to string.
141 * @param cchMax Max length.
142 */
143static unsigned rtStrFormatStrNLenUni(PCRTUNICP pusz, unsigned cchMax)
144{
145 PCRTUNICP puszC = pusz;
146
147 while (cchMax-- > 0 && *pusz != '\0')
148 pusz++;
149
150 return (unsigned)(pusz - puszC);
151}
152
153
154RTDECL(int) RTStrFormatNumber(char *psz, uint64_t u64Value, unsigned int uiBase, signed int cchWidth, signed int cchPrecision,
155 unsigned int fFlags)
156{
157 const char *pachDigits = "0123456789abcdef";
158 char *pszStart = psz;
159 int cchMax;
160 int cchValue;
161 int i;
162 int j;
163 char chSign;
164
165 /*
166 * Validate and adjust input...
167 */
168 Assert(uiBase >= 2 && uiBase <= 16);
169 if (fFlags & RTSTR_F_CAPITAL)
170 pachDigits = "0123456789ABCDEF";
171 if (fFlags & RTSTR_F_LEFT)
172 fFlags &= ~RTSTR_F_ZEROPAD;
173 if ( (fFlags & RTSTR_F_THOUSAND_SEP)
174 && ( uiBase != 10
175 || (fFlags & RTSTR_F_ZEROPAD))) /** @todo implement RTSTR_F_ZEROPAD + RTSTR_F_THOUSAND_SEP. */
176 fFlags &= ~RTSTR_F_THOUSAND_SEP;
177
178 /*
179 * Determine value length and sign. Converts the u64Value to unsigned.
180 */
181 cchValue = 0;
182 chSign = '\0';
183 if ((fFlags & RTSTR_F_64BIT) || (u64Value & UINT64_C(0xffffffff00000000)))
184 {
185 uint64_t u64;
186 if (!(fFlags & RTSTR_F_VALSIGNED) || !(u64Value & RT_BIT_64(63)))
187 u64 = u64Value;
188 else if (u64Value != RT_BIT_64(63))
189 {
190 chSign = '-';
191 u64 = u64Value = -(int64_t)u64Value;
192 }
193 else
194 {
195 chSign = '-';
196 u64 = u64Value = RT_BIT_64(63);
197 }
198 do
199 {
200 cchValue++;
201 u64 /= uiBase;
202 } while (u64);
203 }
204 else
205 {
206 uint32_t u32 = (uint32_t)u64Value;
207 if (!(fFlags & RTSTR_F_VALSIGNED) || !(u32 & UINT32_C(0x80000000)))
208 { /* likley */ }
209 else if (u32 != UINT32_C(0x80000000))
210 {
211 chSign = '-';
212 u64Value = u32 = -(int32_t)u32;
213 }
214 else
215 {
216 chSign = '-';
217 u64Value = u32 = UINT32_C(0x80000000);
218 }
219 do
220 {
221 cchValue++;
222 u32 /= uiBase;
223 } while (u32);
224 }
225 if (fFlags & RTSTR_F_THOUSAND_SEP)
226 {
227 if (cchValue <= 3)
228 fFlags &= ~RTSTR_F_THOUSAND_SEP;
229 else
230 cchValue += cchValue / 3 - (cchValue % 3 == 0);
231 }
232
233 /*
234 * Sign (+/-).
235 */
236 i = 0;
237 if (fFlags & RTSTR_F_VALSIGNED)
238 {
239 if (chSign != '\0')
240 psz[i++] = chSign;
241 else if (fFlags & (RTSTR_F_PLUS | RTSTR_F_BLANK))
242 psz[i++] = (char)(fFlags & RTSTR_F_PLUS ? '+' : ' ');
243 }
244
245 /*
246 * Special (0/0x).
247 */
248 if ((fFlags & RTSTR_F_SPECIAL) && (uiBase % 8) == 0)
249 {
250 psz[i++] = '0';
251 if (uiBase == 16)
252 psz[i++] = (char)(fFlags & RTSTR_F_CAPITAL ? 'X' : 'x');
253 }
254
255 /*
256 * width - only if ZEROPAD
257 */
258 cchMax = 64 - (cchValue + i + 1); /* HACK! 64 bytes seems to be the usual buffer size... */
259 cchWidth -= i + cchValue;
260 if (fFlags & RTSTR_F_ZEROPAD)
261 while (--cchWidth >= 0 && i < cchMax)
262 {
263 AssertBreak(i < cchMax);
264 psz[i++] = '0';
265 cchPrecision--;
266 }
267 else if (!(fFlags & RTSTR_F_LEFT) && cchWidth > 0)
268 {
269 AssertStmt(cchWidth < cchMax, cchWidth = cchMax - 1);
270 for (j = i - 1; j >= 0; j--)
271 psz[cchWidth + j] = psz[j];
272 for (j = 0; j < cchWidth; j++)
273 psz[j] = ' ';
274 i += cchWidth;
275 }
276
277 /*
278 * precision
279 */
280 while (--cchPrecision >= cchValue)
281 {
282 AssertBreak(i < cchMax);
283 psz[i++] = '0';
284 }
285
286 psz += i;
287
288 /*
289 * write number - not good enough but it works
290 */
291 psz += cchValue;
292 i = -1;
293 if ((fFlags & RTSTR_F_64BIT) || (u64Value & UINT64_C(0xffffffff00000000)))
294 {
295 uint64_t u64 = u64Value;
296 if (fFlags & RTSTR_F_THOUSAND_SEP)
297 {
298 do
299 {
300 if ((-i - 1) % 4 == 3)
301 psz[i--] = ' ';
302 psz[i--] = pachDigits[u64 % uiBase];
303 u64 /= uiBase;
304 } while (u64);
305 }
306 else
307 {
308 do
309 {
310 psz[i--] = pachDigits[u64 % uiBase];
311 u64 /= uiBase;
312 } while (u64);
313 }
314 }
315 else
316 {
317 uint32_t u32 = (uint32_t)u64Value;
318 if (fFlags & RTSTR_F_THOUSAND_SEP)
319 {
320 do
321 {
322 if ((-i - 1) % 4 == 3)
323 psz[i--] = ' ';
324 psz[i--] = pachDigits[u32 % uiBase];
325 u32 /= uiBase;
326 } while (u32);
327 }
328 else
329 {
330 do
331 {
332 psz[i--] = pachDigits[u32 % uiBase];
333 u32 /= uiBase;
334 } while (u32);
335 }
336 }
337
338 /*
339 * width if RTSTR_F_LEFT
340 */
341 if (fFlags & RTSTR_F_LEFT)
342 while (--cchWidth >= 0)
343 *psz++ = ' ';
344
345 *psz = '\0';
346 return (unsigned)(psz - pszStart);
347}
348RT_EXPORT_SYMBOL(RTStrFormatNumber);
349
350
351RTDECL(size_t) RTStrFormatV(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat,
352 const char *pszFormat, va_list InArgs)
353{
354 char szTmp[64]; /* Worker functions assumes 64 byte buffer! Ugly but faster. */
355 size_t cch = 0;
356 const char *pszStartOutput = pszFormat;
357
358 /* make a local copy so we can reference it (AMD64 / gcc). */
359 va_list args;
360 va_copy(args, InArgs);
361
362 while (*pszFormat != '\0')
363 {
364 if (*pszFormat == '%')
365 {
366 /* output pending string. */
367 if (pszStartOutput != pszFormat)
368 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
369
370 /* skip '%' */
371 pszFormat++;
372 if (*pszFormat == '%') /* '%%'-> '%' */
373 pszStartOutput = pszFormat++;
374 else
375 {
376 unsigned int fFlags = 0;
377 int cchWidth = -1;
378 int cchPrecision = -1;
379 unsigned int uBase = 10;
380 char chArgSize;
381
382 /* flags */
383 for (;;)
384 {
385 switch (*pszFormat++)
386 {
387 case '#': fFlags |= RTSTR_F_SPECIAL; continue;
388 case '-': fFlags |= RTSTR_F_LEFT; continue;
389 case '+': fFlags |= RTSTR_F_PLUS; continue;
390 case ' ': fFlags |= RTSTR_F_BLANK; continue;
391 case '0': fFlags |= RTSTR_F_ZEROPAD; continue;
392 case '\'': fFlags |= RTSTR_F_THOUSAND_SEP; continue;
393 }
394 pszFormat--;
395 break;
396 }
397
398 /* width */
399 if (RT_C_IS_DIGIT(*pszFormat))
400 {
401 for (cchWidth = 0; RT_C_IS_DIGIT(*pszFormat); pszFormat++)
402 {
403 cchWidth *= 10;
404 cchWidth += *pszFormat - '0';
405 }
406 fFlags |= RTSTR_F_WIDTH;
407 }
408 else if (*pszFormat == '*')
409 {
410 pszFormat++;
411 cchWidth = va_arg(args, int);
412 if (cchWidth < 0)
413 {
414 cchWidth = -cchWidth;
415 fFlags |= RTSTR_F_LEFT;
416 }
417 fFlags |= RTSTR_F_WIDTH;
418 }
419
420 /* precision */
421 if (*pszFormat == '.')
422 {
423 pszFormat++;
424 if (RT_C_IS_DIGIT(*pszFormat))
425 {
426 for (cchPrecision = 0; RT_C_IS_DIGIT(*pszFormat); pszFormat++)
427 {
428 cchPrecision *= 10;
429 cchPrecision += *pszFormat - '0';
430 }
431
432 }
433 else if (*pszFormat == '*')
434 {
435 pszFormat++;
436 cchPrecision = va_arg(args, int);
437 }
438 if (cchPrecision < 0)
439 cchPrecision = 0;
440 fFlags |= RTSTR_F_PRECISION;
441 }
442
443 /*
444 * Argument size.
445 */
446 chArgSize = *pszFormat;
447 switch (chArgSize)
448 {
449 default:
450 chArgSize = 0;
451 break;
452
453 case 'z':
454 case 'L':
455 case 'j':
456 case 't':
457 pszFormat++;
458 break;
459
460 case 'l':
461 pszFormat++;
462 if (*pszFormat == 'l')
463 {
464 chArgSize = 'L';
465 pszFormat++;
466 }
467 break;
468
469 case 'h':
470 pszFormat++;
471 if (*pszFormat == 'h')
472 {
473 chArgSize = 'H';
474 pszFormat++;
475 }
476 break;
477
478 case 'I': /* Used by Win32/64 compilers. */
479 if ( pszFormat[1] == '6'
480 && pszFormat[2] == '4')
481 {
482 pszFormat += 3;
483 chArgSize = 'L';
484 }
485 else if ( pszFormat[1] == '3'
486 && pszFormat[2] == '2')
487 {
488 pszFormat += 3;
489 chArgSize = 0;
490 }
491 else
492 {
493 pszFormat += 1;
494 chArgSize = 'j';
495 }
496 break;
497
498 case 'q': /* Used on BSD platforms. */
499 pszFormat++;
500 chArgSize = 'L';
501 break;
502 }
503
504 /*
505 * The type.
506 */
507 switch (*pszFormat++)
508 {
509 /* char */
510 case 'c':
511 {
512 if (!(fFlags & RTSTR_F_LEFT))
513 while (--cchWidth > 0)
514 cch += pfnOutput(pvArgOutput, " ", 1);
515
516 szTmp[0] = (char)va_arg(args, int);
517 szTmp[1] = '\0'; /* Some output functions wants terminated strings. */
518 cch += pfnOutput(pvArgOutput, &szTmp[0], 1);
519
520 while (--cchWidth > 0)
521 cch += pfnOutput(pvArgOutput, " ", 1);
522 break;
523 }
524
525 case 'S': /* Legacy, conversion done by streams now. */
526 case 's':
527 {
528 if (chArgSize == 'l')
529 {
530 /* utf-16 -> utf-8 */
531 PCRTUTF16 pwszStr = va_arg(args, PCRTUTF16);
532 if (RT_VALID_PTR(pwszStr))
533 {
534 int cwcStr = rtStrFormatStrNLenUtf16(pwszStr, (unsigned)cchPrecision);
535 if (!(fFlags & RTSTR_F_LEFT))
536 while (--cchWidth >= cwcStr)
537 cch += pfnOutput(pvArgOutput, " ", 1);
538 cchWidth -= cwcStr;
539 while (cwcStr-- > 0)
540 {
541/** @todo \#ifndef IN_RC*/
542#ifdef IN_RING3
543 RTUNICP Cp;
544 RTUtf16GetCpEx(&pwszStr, &Cp);
545 char *pszEnd = RTStrPutCp(szTmp, Cp);
546 *pszEnd = '\0';
547 cch += pfnOutput(pvArgOutput, szTmp, pszEnd - szTmp);
548#else
549 char ch = (char)*pwszStr++;
550 cch += pfnOutput(pvArgOutput, &ch, 1);
551#endif
552 }
553 while (--cchWidth >= 0)
554 cch += pfnOutput(pvArgOutput, " ", 1);
555 }
556 else
557 cch = rtStrFormatBadPointer(cch, pfnOutput, pvArgOutput, cchWidth, fFlags,
558 pwszStr, szTmp, RT_STR_TUPLE("!BadStrW"));
559 }
560 else if (chArgSize == 'L')
561 {
562 /* unicp -> utf8 */
563 PCRTUNICP puszStr = va_arg(args, PCRTUNICP);
564 if (RT_VALID_PTR(puszStr))
565 {
566 int cchStr = rtStrFormatStrNLenUni(puszStr, (unsigned)cchPrecision);
567 if (!(fFlags & RTSTR_F_LEFT))
568 while (--cchWidth >= cchStr)
569 cch += pfnOutput(pvArgOutput, " ", 1);
570
571 cchWidth -= cchStr;
572 while (cchStr-- > 0)
573 {
574/** @todo \#ifndef IN_RC*/
575#ifdef IN_RING3
576 char *pszEnd = RTStrPutCp(szTmp, *puszStr++);
577 cch += pfnOutput(pvArgOutput, szTmp, pszEnd - szTmp);
578#else
579 char ch = (char)*puszStr++;
580 cch += pfnOutput(pvArgOutput, &ch, 1);
581#endif
582 }
583 while (--cchWidth >= 0)
584 cch += pfnOutput(pvArgOutput, " ", 1);
585 }
586 else
587 cch = rtStrFormatBadPointer(cch, pfnOutput, pvArgOutput, cchWidth, fFlags,
588 puszStr, szTmp, RT_STR_TUPLE("!BadStrU"));
589 }
590 else
591 {
592 const char *pszStr = va_arg(args, const char *);
593 if (RT_VALID_PTR(pszStr))
594 {
595 int cchStr = rtStrFormatStrNLen(pszStr, (unsigned)cchPrecision);
596 if (!(fFlags & RTSTR_F_LEFT))
597 while (--cchWidth >= cchStr)
598 cch += pfnOutput(pvArgOutput, " ", 1);
599
600 cch += pfnOutput(pvArgOutput, pszStr, cchStr);
601
602 while (--cchWidth >= cchStr)
603 cch += pfnOutput(pvArgOutput, " ", 1);
604 }
605 else
606 cch = rtStrFormatBadPointer(cch, pfnOutput, pvArgOutput, cchWidth, fFlags,
607 pszStr, szTmp, RT_STR_TUPLE("!BadStr"));
608 }
609 break;
610 }
611
612 /*-----------------*/
613 /* integer/pointer */
614 /*-----------------*/
615 case 'd':
616 case 'i':
617 case 'o':
618 case 'p':
619 case 'u':
620 case 'x':
621 case 'X':
622 {
623 int cchNum;
624 uint64_t u64Value;
625
626 switch (pszFormat[-1])
627 {
628 case 'd': /* signed decimal integer */
629 case 'i':
630 fFlags |= RTSTR_F_VALSIGNED;
631 break;
632
633 case 'o':
634 uBase = 8;
635 break;
636
637 case 'p':
638 fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
639 uBase = 16;
640 if (cchWidth < 0)
641 cchWidth = sizeof(char *) * 2;
642 break;
643
644 case 'u':
645 uBase = 10;
646 break;
647
648 case 'X':
649 fFlags |= RTSTR_F_CAPITAL;
650 RT_FALL_THRU();
651 case 'x':
652 uBase = 16;
653 break;
654 }
655
656 if (pszFormat[-1] == 'p')
657 u64Value = va_arg(args, uintptr_t);
658 else if (fFlags & RTSTR_F_VALSIGNED)
659 {
660 if (chArgSize == 'L')
661 {
662 u64Value = va_arg(args, int64_t);
663 fFlags |= RTSTR_F_64BIT;
664 }
665 else if (chArgSize == 'l')
666 {
667 u64Value = va_arg(args, signed long);
668 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
669 }
670 else if (chArgSize == 'h')
671 {
672 u64Value = va_arg(args, /* signed short */ int);
673 fFlags |= RTSTR_GET_BIT_FLAG(signed short);
674 }
675 else if (chArgSize == 'H')
676 {
677 u64Value = va_arg(args, /* int8_t */ int);
678 fFlags |= RTSTR_GET_BIT_FLAG(int8_t);
679 }
680 else if (chArgSize == 'j')
681 {
682 u64Value = va_arg(args, /*intmax_t*/ int64_t);
683 fFlags |= RTSTR_F_64BIT;
684 }
685 else if (chArgSize == 'z')
686 {
687 u64Value = va_arg(args, size_t);
688 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
689 }
690 else if (chArgSize == 't')
691 {
692 u64Value = va_arg(args, ptrdiff_t);
693 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
694 }
695 else
696 {
697 u64Value = va_arg(args, signed int);
698 fFlags |= RTSTR_GET_BIT_FLAG(signed int);
699 }
700 }
701 else
702 {
703 if (chArgSize == 'L')
704 {
705 u64Value = va_arg(args, uint64_t);
706 fFlags |= RTSTR_F_64BIT;
707 }
708 else if (chArgSize == 'l')
709 {
710 u64Value = va_arg(args, unsigned long);
711 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
712 }
713 else if (chArgSize == 'h')
714 {
715 u64Value = va_arg(args, /* unsigned short */ int);
716 fFlags |= RTSTR_GET_BIT_FLAG(unsigned short);
717 }
718 else if (chArgSize == 'H')
719 {
720 u64Value = va_arg(args, /* uint8_t */ int);
721 fFlags |= RTSTR_GET_BIT_FLAG(uint8_t);
722 }
723 else if (chArgSize == 'j')
724 {
725 u64Value = va_arg(args, /*uintmax_t*/ int64_t);
726 fFlags |= RTSTR_F_64BIT;
727 }
728 else if (chArgSize == 'z')
729 {
730 u64Value = va_arg(args, size_t);
731 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
732 }
733 else if (chArgSize == 't')
734 {
735 u64Value = va_arg(args, ptrdiff_t);
736 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
737 }
738 else
739 {
740 u64Value = va_arg(args, unsigned int);
741 fFlags |= RTSTR_GET_BIT_FLAG(unsigned int);
742 }
743 }
744 cchNum = RTStrFormatNumber(&szTmp[0], u64Value, uBase, cchWidth, cchPrecision, fFlags);
745 cch += pfnOutput(pvArgOutput, &szTmp[0], cchNum);
746 break;
747 }
748
749 /*
750 * Floating point.
751 *
752 * We currently don't really implement these yet, there is just a very basic
753 * formatting regardless of the requested type.
754 */
755 case 'e': /* [-]d.dddddde+-dd[d] */
756 case 'E': /* [-]d.ddddddE+-dd[d] */
757 case 'f': /* [-]dddd.dddddd / inf / nan */
758 case 'F': /* [-]dddd.dddddd / INF / NAN */
759 case 'g': /* Either f or e, depending on the magnitue and precision. */
760 case 'G': /* Either f or E, depending on the magnitue and precision. */
761 case 'a': /* [-]0xh.hhhhhhp+-dd */
762 case 'A': /* [-]0Xh.hhhhhhP+-dd */
763 {
764#if defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3) && !defined(IPRT_NO_FLOAT_FORMATTING)
765 size_t cchNum;
766# ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
767 if (chArgSize == 'L')
768 {
769 RTFLOAT80U2 r80;
770 r80.lrd = va_arg(args, long double);
771# ifndef IN_BLD_PROG
772 cchNum = RTStrFormatR80u2(&szTmp[0], sizeof(szTmp), &r80, cchWidth, cchPrecision, 0);
773# else
774 cch += pfnOutput(pvArgOutput, RT_STR_TUPLE("<long double>"));
775 RT_NOREF_PV(r80);
776 break;
777# endif
778 }
779 else
780# endif
781 {
782 RTFLOAT64U r64;
783 r64.rd = va_arg(args, double);
784# ifndef IN_BLD_PROG
785 cchNum = RTStrFormatR64(&szTmp[0], sizeof(szTmp), &r64, cchWidth, cchPrecision, 0);
786# else
787 cchNum = RTStrFormatNumber(&szTmp[0], r64.au64[0], 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_64BIT);
788# endif
789 }
790 cch += pfnOutput(pvArgOutput, &szTmp[0], cchNum);
791#else /* !IN_RING3 */
792 AssertFailed();
793#endif /* !IN_RING3 */
794 break;
795 }
796
797 /*
798 * Nested extensions.
799 */
800 case 'M': /* replace the format string (not stacked yet). */
801 {
802 pszStartOutput = pszFormat = va_arg(args, const char *);
803 AssertPtr(pszStartOutput);
804 break;
805 }
806
807 case 'N': /* real nesting. */
808 {
809 const char *pszFormatNested = va_arg(args, const char *);
810 va_list *pArgsNested = va_arg(args, va_list *);
811 va_list ArgsNested;
812 va_copy(ArgsNested, *pArgsNested);
813 Assert(pszFormatNested);
814 cch += RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormatNested, ArgsNested);
815 va_end(ArgsNested);
816 break;
817 }
818
819 /*
820 * IPRT Extensions.
821 */
822 case 'R':
823 {
824 if (*pszFormat != '[')
825 {
826 pszFormat--;
827 cch += rtstrFormatRt(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
828 }
829 else
830 {
831 pszFormat--;
832 cch += rtstrFormatType(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
833 }
834 break;
835 }
836
837 /*
838 * Custom format.
839 */
840 default:
841 {
842 if (pfnFormat)
843 {
844 pszFormat--;
845 cch += pfnFormat(pvArgFormat, pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
846 }
847 break;
848 }
849 }
850 pszStartOutput = pszFormat;
851 }
852 }
853 else
854 pszFormat++;
855 }
856
857 /* output pending string. */
858 if (pszStartOutput != pszFormat)
859 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
860
861 /* terminate the output */
862 pfnOutput(pvArgOutput, NULL, 0);
863
864 va_end(args);
865 return cch;
866}
867RT_EXPORT_SYMBOL(RTStrFormatV);
868
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