VirtualBox

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

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

IPRT: strformat.cpp: Share the temporary buffer and use it for '%c' output buffering to prevent trouble if the output callback checks for null 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 59703 2016-02-16 13:50:10Z 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.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 cch += pfnOutput(pvArgOutput, szTmp, pszEnd - szTmp);
563#else
564 char ch = (char)*pwszStr++;
565 cch += pfnOutput(pvArgOutput, &ch, 1);
566#endif
567 }
568 while (--cchWidth >= 0)
569 cch += pfnOutput(pvArgOutput, " ", 1);
570 }
571 else if (chArgSize == 'L')
572 {
573 /* unicp -> utf8 */
574 int cchStr;
575 PCRTUNICP puszStr = va_arg(args, PCRTUNICP);
576
577 if (!VALID_PTR(puszStr))
578 {
579 static RTUNICP s_uszNull[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
580 puszStr = s_uszNull;
581 }
582 cchStr = _strnlenUni(puszStr, (unsigned)cchPrecision);
583 if (!(fFlags & RTSTR_F_LEFT))
584 while (--cchWidth >= cchStr)
585 cch += pfnOutput(pvArgOutput, " ", 1);
586
587 cchWidth -= cchStr;
588 while (cchStr-- > 0)
589 {
590/**@todo \#ifndef IN_RC*/
591#ifdef IN_RING3
592 char *pszEnd = RTStrPutCp(szTmp, *puszStr++);
593 cch += pfnOutput(pvArgOutput, szTmp, pszEnd - szTmp);
594#else
595 char ch = (char)*puszStr++;
596 cch += pfnOutput(pvArgOutput, &ch, 1);
597#endif
598 }
599 while (--cchWidth >= 0)
600 cch += pfnOutput(pvArgOutput, " ", 1);
601 }
602 else
603 {
604 int cchStr;
605 const char *pszStr = va_arg(args, char*);
606
607 if (!VALID_PTR(pszStr))
608 pszStr = "<NULL>";
609 cchStr = _strnlen(pszStr, (unsigned)cchPrecision);
610 if (!(fFlags & RTSTR_F_LEFT))
611 while (--cchWidth >= cchStr)
612 cch += pfnOutput(pvArgOutput, " ", 1);
613
614 cch += pfnOutput(pvArgOutput, pszStr, cchStr);
615
616 while (--cchWidth >= cchStr)
617 cch += pfnOutput(pvArgOutput, " ", 1);
618 }
619 break;
620 }
621
622 /*-----------------*/
623 /* integer/pointer */
624 /*-----------------*/
625 case 'd':
626 case 'i':
627 case 'o':
628 case 'p':
629 case 'u':
630 case 'x':
631 case 'X':
632 {
633 int cchNum;
634 uint64_t u64Value;
635
636 switch (pszFormat[-1])
637 {
638 case 'd': /* signed decimal integer */
639 case 'i':
640 fFlags |= RTSTR_F_VALSIGNED;
641 break;
642
643 case 'o':
644 uBase = 8;
645 break;
646
647 case 'p':
648 fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
649 uBase = 16;
650 if (cchWidth < 0)
651 cchWidth = sizeof(char *) * 2;
652 break;
653
654 case 'u':
655 uBase = 10;
656 break;
657
658 case 'X':
659 fFlags |= RTSTR_F_CAPITAL;
660 case 'x':
661 uBase = 16;
662 break;
663 }
664
665 if (pszFormat[-1] == 'p')
666 u64Value = va_arg(args, uintptr_t);
667 else if (fFlags & RTSTR_F_VALSIGNED)
668 {
669 if (chArgSize == 'L')
670 {
671 u64Value = va_arg(args, int64_t);
672 fFlags |= RTSTR_F_64BIT;
673 }
674 else if (chArgSize == 'l')
675 {
676 u64Value = va_arg(args, signed long);
677 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
678 }
679 else if (chArgSize == 'h')
680 {
681 u64Value = va_arg(args, /* signed short */ int);
682 fFlags |= RTSTR_GET_BIT_FLAG(signed short);
683 }
684 else if (chArgSize == 'H')
685 {
686 u64Value = va_arg(args, /* int8_t */ int);
687 fFlags |= RTSTR_GET_BIT_FLAG(int8_t);
688 }
689 else if (chArgSize == 'j')
690 {
691 u64Value = va_arg(args, /*intmax_t*/ int64_t);
692 fFlags |= RTSTR_F_64BIT;
693 }
694 else if (chArgSize == 'z')
695 {
696 u64Value = va_arg(args, size_t);
697 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
698 }
699 else if (chArgSize == 't')
700 {
701 u64Value = va_arg(args, ptrdiff_t);
702 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
703 }
704 else
705 {
706 u64Value = va_arg(args, signed int);
707 fFlags |= RTSTR_GET_BIT_FLAG(signed int);
708 }
709 }
710 else
711 {
712 if (chArgSize == 'L')
713 {
714 u64Value = va_arg(args, uint64_t);
715 fFlags |= RTSTR_F_64BIT;
716 }
717 else if (chArgSize == 'l')
718 {
719 u64Value = va_arg(args, unsigned long);
720 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
721 }
722 else if (chArgSize == 'h')
723 {
724 u64Value = va_arg(args, /* unsigned short */ int);
725 fFlags |= RTSTR_GET_BIT_FLAG(unsigned short);
726 }
727 else if (chArgSize == 'H')
728 {
729 u64Value = va_arg(args, /* uint8_t */ int);
730 fFlags |= RTSTR_GET_BIT_FLAG(uint8_t);
731 }
732 else if (chArgSize == 'j')
733 {
734 u64Value = va_arg(args, /*uintmax_t*/ int64_t);
735 fFlags |= RTSTR_F_64BIT;
736 }
737 else if (chArgSize == 'z')
738 {
739 u64Value = va_arg(args, size_t);
740 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
741 }
742 else if (chArgSize == 't')
743 {
744 u64Value = va_arg(args, ptrdiff_t);
745 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
746 }
747 else
748 {
749 u64Value = va_arg(args, unsigned int);
750 fFlags |= RTSTR_GET_BIT_FLAG(unsigned int);
751 }
752 }
753 cchNum = RTStrFormatNumber((char *)SSToDS(&szTmp), u64Value, uBase, cchWidth, cchPrecision, fFlags);
754 cch += pfnOutput(pvArgOutput, (char *)SSToDS(&szTmp), cchNum);
755 break;
756 }
757
758 /*
759 * Nested extensions.
760 */
761 case 'M': /* replace the format string (not stacked yet). */
762 {
763 pszStartOutput = pszFormat = va_arg(args, const char *);
764 AssertPtr(pszStartOutput);
765 break;
766 }
767
768 case 'N': /* real nesting. */
769 {
770 const char *pszFormatNested = va_arg(args, const char *);
771 va_list *pArgsNested = va_arg(args, va_list *);
772 va_list ArgsNested;
773 va_copy(ArgsNested, *pArgsNested);
774 Assert(pszFormatNested);
775 cch += RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormatNested, ArgsNested);
776 va_end(ArgsNested);
777 break;
778 }
779
780 /*
781 * IPRT Extensions.
782 */
783 case 'R':
784 {
785 if (*pszFormat != '[')
786 {
787 pszFormat--;
788 cch += rtstrFormatRt(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
789 }
790 else
791 {
792 pszFormat--;
793 cch += rtstrFormatType(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
794 }
795 break;
796 }
797
798 /*
799 * Custom format.
800 */
801 default:
802 {
803 if (pfnFormat)
804 {
805 pszFormat--;
806 cch += pfnFormat(pvArgFormat, pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
807 }
808 break;
809 }
810 }
811 pszStartOutput = pszFormat;
812 }
813 }
814 else
815 pszFormat++;
816 }
817
818 /* output pending string. */
819 if (pszStartOutput != pszFormat)
820 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
821
822 /* terminate the output */
823 pfnOutput(pvArgOutput, NULL, 0);
824
825 return cch;
826}
827RT_EXPORT_SYMBOL(RTStrFormatV);
828
829
830/**
831 * Partial implementation of a printf like formatter.
832 * It doesn't do everything correct, and there is no floating point support.
833 * However, it supports custom formats by the means of a format callback.
834 *
835 * @returns number of bytes formatted.
836 * @param pfnOutput Output worker.
837 * Called in two ways. Normally with a string an it's length.
838 * For termination, it's called with NULL for string, 0 for length.
839 * @param pvArgOutput Argument to the output worker.
840 * @param pfnFormat Custom format worker.
841 * @param pvArgFormat Argument to the format worker.
842 * @param pszFormat Format string.
843 * @param ... Argument list.
844 */
845RTDECL(size_t) RTStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, const char *pszFormat, ...)
846{
847 size_t cch;
848 va_list args;
849 va_start(args, pszFormat);
850 cch = RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormat, args);
851 va_end(args);
852 return cch;
853}
854RT_EXPORT_SYMBOL(RTStrFormat);
855
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