VirtualBox

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

Last change on this file since 69046 was 69046, checked in by vboxsync, 7 years ago

Global: replace fall-through comments with RT_FALL_THRU().
bugref:8192: gcc warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 30.4 KB
Line 
1/* $Id: strformat.cpp 69046 2017-10-11 16:11:23Z vboxsync $ */
2/** @file
3 * IPRT - String Formatter.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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* 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 RT_FALL_THRU();
662 case 'x':
663 uBase = 16;
664 break;
665 }
666
667 if (pszFormat[-1] == 'p')
668 u64Value = va_arg(args, uintptr_t);
669 else if (fFlags & RTSTR_F_VALSIGNED)
670 {
671 if (chArgSize == 'L')
672 {
673 u64Value = va_arg(args, int64_t);
674 fFlags |= RTSTR_F_64BIT;
675 }
676 else if (chArgSize == 'l')
677 {
678 u64Value = va_arg(args, signed long);
679 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
680 }
681 else if (chArgSize == 'h')
682 {
683 u64Value = va_arg(args, /* signed short */ int);
684 fFlags |= RTSTR_GET_BIT_FLAG(signed short);
685 }
686 else if (chArgSize == 'H')
687 {
688 u64Value = va_arg(args, /* int8_t */ int);
689 fFlags |= RTSTR_GET_BIT_FLAG(int8_t);
690 }
691 else if (chArgSize == 'j')
692 {
693 u64Value = va_arg(args, /*intmax_t*/ int64_t);
694 fFlags |= RTSTR_F_64BIT;
695 }
696 else if (chArgSize == 'z')
697 {
698 u64Value = va_arg(args, size_t);
699 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
700 }
701 else if (chArgSize == 't')
702 {
703 u64Value = va_arg(args, ptrdiff_t);
704 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
705 }
706 else
707 {
708 u64Value = va_arg(args, signed int);
709 fFlags |= RTSTR_GET_BIT_FLAG(signed int);
710 }
711 }
712 else
713 {
714 if (chArgSize == 'L')
715 {
716 u64Value = va_arg(args, uint64_t);
717 fFlags |= RTSTR_F_64BIT;
718 }
719 else if (chArgSize == 'l')
720 {
721 u64Value = va_arg(args, unsigned long);
722 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
723 }
724 else if (chArgSize == 'h')
725 {
726 u64Value = va_arg(args, /* unsigned short */ int);
727 fFlags |= RTSTR_GET_BIT_FLAG(unsigned short);
728 }
729 else if (chArgSize == 'H')
730 {
731 u64Value = va_arg(args, /* uint8_t */ int);
732 fFlags |= RTSTR_GET_BIT_FLAG(uint8_t);
733 }
734 else if (chArgSize == 'j')
735 {
736 u64Value = va_arg(args, /*uintmax_t*/ int64_t);
737 fFlags |= RTSTR_F_64BIT;
738 }
739 else if (chArgSize == 'z')
740 {
741 u64Value = va_arg(args, size_t);
742 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
743 }
744 else if (chArgSize == 't')
745 {
746 u64Value = va_arg(args, ptrdiff_t);
747 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
748 }
749 else
750 {
751 u64Value = va_arg(args, unsigned int);
752 fFlags |= RTSTR_GET_BIT_FLAG(unsigned int);
753 }
754 }
755 cchNum = RTStrFormatNumber((char *)SSToDS(&szTmp), u64Value, uBase, cchWidth, cchPrecision, fFlags);
756 cch += pfnOutput(pvArgOutput, (char *)SSToDS(&szTmp), cchNum);
757 break;
758 }
759
760 /*
761 * Nested extensions.
762 */
763 case 'M': /* replace the format string (not stacked yet). */
764 {
765 pszStartOutput = pszFormat = va_arg(args, const char *);
766 AssertPtr(pszStartOutput);
767 break;
768 }
769
770 case 'N': /* real nesting. */
771 {
772 const char *pszFormatNested = va_arg(args, const char *);
773 va_list *pArgsNested = va_arg(args, va_list *);
774 va_list ArgsNested;
775 va_copy(ArgsNested, *pArgsNested);
776 Assert(pszFormatNested);
777 cch += RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormatNested, ArgsNested);
778 va_end(ArgsNested);
779 break;
780 }
781
782 /*
783 * IPRT Extensions.
784 */
785 case 'R':
786 {
787 if (*pszFormat != '[')
788 {
789 pszFormat--;
790 cch += rtstrFormatRt(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
791 }
792 else
793 {
794 pszFormat--;
795 cch += rtstrFormatType(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
796 }
797 break;
798 }
799
800 /*
801 * Custom format.
802 */
803 default:
804 {
805 if (pfnFormat)
806 {
807 pszFormat--;
808 cch += pfnFormat(pvArgFormat, pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
809 }
810 break;
811 }
812 }
813 pszStartOutput = pszFormat;
814 }
815 }
816 else
817 pszFormat++;
818 }
819
820 /* output pending string. */
821 if (pszStartOutput != pszFormat)
822 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
823
824 /* terminate the output */
825 pfnOutput(pvArgOutput, NULL, 0);
826
827 return cch;
828}
829RT_EXPORT_SYMBOL(RTStrFormatV);
830
831
832/**
833 * Partial implementation of a printf like formatter.
834 * It doesn't do everything correct, and there is no floating point support.
835 * However, it supports custom formats by the means of a format callback.
836 *
837 * @returns number of bytes formatted.
838 * @param pfnOutput Output worker.
839 * Called in two ways. Normally with a string an it's length.
840 * For termination, it's called with NULL for string, 0 for length.
841 * @param pvArgOutput Argument to the output worker.
842 * @param pfnFormat Custom format worker.
843 * @param pvArgFormat Argument to the format worker.
844 * @param pszFormat Format string.
845 * @param ... Argument list.
846 */
847RTDECL(size_t) RTStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, const char *pszFormat, ...)
848{
849 size_t cch;
850 va_list args;
851 va_start(args, pszFormat);
852 cch = RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormat, args);
853 va_end(args);
854 return cch;
855}
856RT_EXPORT_SYMBOL(RTStrFormat);
857
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