]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - ubuntu/vbox/vboxguest/common/string/strformat.c
UBUNTU: ubuntu: vbox -- update to 5.1.6-dfsg-1
[mirror_ubuntu-zesty-kernel.git] / ubuntu / vbox / vboxguest / common / string / strformat.c
1 /* $Id: strformat.cpp $ */
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
59 typedef struct
60 {
61 uint32_t ulLo;
62 uint32_t ulHi;
63 } KSIZE64;
64
65
66 /*********************************************************************************************************************************
67 * Internal Functions *
68 *********************************************************************************************************************************/
69 static unsigned _strnlen(const char *psz, unsigned cchMax);
70 static unsigned _strnlenUtf16(PCRTUTF16 pwsz, unsigned cchMax);
71 static 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 */
80 static 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 */
97 static 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 */
128 static 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 */
150 RTDECL(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 }
155 RT_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 */
170 static 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 */
363 RTDECL(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 }
828 RT_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 */
846 RTDECL(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 }
855 RT_EXPORT_SYMBOL(RTStrFormat);
856