VirtualBox

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

Last change on this file since 62164 was 62164, checked in by vboxsync, 9 years ago

Runtime: make sure the string passed to pfnOutput is zero terminated, as the code assumes it is the case (no serious failure if it isn't, but it triggers useless string duplication which is far more expensive than the zero termination)

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

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