VirtualBox

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

Last change on this file since 40186 was 40186, checked in by vboxsync, 13 years ago

RTStrFormatNumber: Assert some buffer sanity.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 28.6 KB
Line 
1/* $Id: strformat.cpp 40186 2012-02-21 00:04:21Z vboxsync $ */
2/** @file
3 * IPRT - String Formatter.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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 va_list args;
367 KSIZE cch = 0;
368 const char *pszStartOutput = pszFormat;
369
370 va_copy(args, InArgs); /* make a copy so we can reference it (AMD64 / gcc). */
371
372 while (*pszFormat != '\0')
373 {
374 if (*pszFormat == '%')
375 {
376 /* output pending string. */
377 if (pszStartOutput != pszFormat)
378 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
379
380 /* skip '%' */
381 pszFormat++;
382 if (*pszFormat == '%') /* '%%'-> '%' */
383 pszStartOutput = pszFormat++;
384 else
385 {
386 unsigned int fFlags = 0;
387 int cchWidth = -1;
388 int cchPrecision = -1;
389 unsigned int uBase = 10;
390 char chArgSize;
391
392 /* flags */
393 for (;;)
394 {
395 switch (*pszFormat++)
396 {
397 case '#': fFlags |= RTSTR_F_SPECIAL; continue;
398 case '-': fFlags |= RTSTR_F_LEFT; continue;
399 case '+': fFlags |= RTSTR_F_PLUS; continue;
400 case ' ': fFlags |= RTSTR_F_BLANK; continue;
401 case '0': fFlags |= RTSTR_F_ZEROPAD; continue;
402 case '\'': fFlags |= RTSTR_F_THOUSAND_SEP; continue;
403 }
404 pszFormat--;
405 break;
406 }
407
408 /* width */
409 if (ISDIGIT(*pszFormat))
410 {
411 for (cchWidth = 0; ISDIGIT(*pszFormat); pszFormat++)
412 {
413 cchWidth *= 10;
414 cchWidth += *pszFormat - '0';
415 }
416 fFlags |= RTSTR_F_WIDTH;
417 }
418 else if (*pszFormat == '*')
419 {
420 pszFormat++;
421 cchWidth = va_arg(args, int);
422 if (cchWidth < 0)
423 {
424 cchWidth = -cchWidth;
425 fFlags |= RTSTR_F_LEFT;
426 }
427 fFlags |= RTSTR_F_WIDTH;
428 }
429
430 /* precision */
431 if (*pszFormat == '.')
432 {
433 pszFormat++;
434 if (ISDIGIT(*pszFormat))
435 {
436 for (cchPrecision = 0; ISDIGIT(*pszFormat); pszFormat++)
437 {
438 cchPrecision *= 10;
439 cchPrecision += *pszFormat - '0';
440 }
441
442 }
443 else if (*pszFormat == '*')
444 {
445 pszFormat++;
446 cchPrecision = va_arg(args, int);
447 }
448 if (cchPrecision < 0)
449 cchPrecision = 0;
450 fFlags |= RTSTR_F_PRECISION;
451 }
452
453 /* argsize */
454 chArgSize = *pszFormat;
455 if (chArgSize != 'l' && chArgSize != 'L' && chArgSize != 'h' && chArgSize != 'j' && chArgSize != 'z' && chArgSize != 't')
456 chArgSize = 0;
457 else
458 {
459 pszFormat++;
460 if (*pszFormat == 'l' && chArgSize == 'l')
461 {
462 chArgSize = 'L';
463 pszFormat++;
464 }
465 else if (*pszFormat == 'h' && chArgSize == 'h')
466 {
467 chArgSize = 'H';
468 pszFormat++;
469 }
470 }
471
472 /*
473 * The type.
474 */
475 switch (*pszFormat++)
476 {
477 /* char */
478 case 'c':
479 {
480 char ch;
481
482 if (!(fFlags & RTSTR_F_LEFT))
483 while (--cchWidth > 0)
484 cch += pfnOutput(pvArgOutput, " ", 1);
485
486 ch = (char)va_arg(args, int);
487 cch += pfnOutput(pvArgOutput, SSToDS(&ch), 1);
488
489 while (--cchWidth > 0)
490 cch += pfnOutput(pvArgOutput, " ", 1);
491 break;
492 }
493
494 case 'S': /* Legacy, conversion done by streams now. */
495 case 's':
496 {
497 if (chArgSize == 'l')
498 {
499 /* utf-16 -> utf-8 */
500 int cchStr;
501 PCRTUTF16 pwszStr = va_arg(args, PRTUTF16);
502
503 if (!VALID_PTR(pwszStr))
504 {
505 static RTUTF16 s_wszNull[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
506 pwszStr = s_wszNull;
507 }
508 cchStr = _strnlenUtf16(pwszStr, (unsigned)cchPrecision);
509 if (!(fFlags & RTSTR_F_LEFT))
510 while (--cchWidth >= cchStr)
511 cch += pfnOutput(pvArgOutput, " ", 1);
512 cchWidth -= cchStr;
513 while (cchStr-- > 0)
514 {
515/**@todo #ifndef IN_RC*/
516#ifdef IN_RING3
517 RTUNICP Cp;
518 RTUtf16GetCpEx(&pwszStr, &Cp);
519 char szUtf8[8]; /* Cp=0x7fffffff -> 6 bytes. */
520 char *pszEnd = RTStrPutCp(szUtf8, Cp);
521 cch += pfnOutput(pvArgOutput, szUtf8, pszEnd - szUtf8);
522#else
523 char ch = (char)*pwszStr++;
524 cch += pfnOutput(pvArgOutput, &ch, 1);
525#endif
526 }
527 while (--cchWidth >= 0)
528 cch += pfnOutput(pvArgOutput, " ", 1);
529 }
530 else if (chArgSize == 'L')
531 {
532 /* unicp -> utf8 */
533 int cchStr;
534 PCRTUNICP puszStr = va_arg(args, PCRTUNICP);
535
536 if (!VALID_PTR(puszStr))
537 {
538 static RTUNICP s_uszNull[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
539 puszStr = s_uszNull;
540 }
541 cchStr = _strnlenUni(puszStr, (unsigned)cchPrecision);
542 if (!(fFlags & RTSTR_F_LEFT))
543 while (--cchWidth >= cchStr)
544 cch += pfnOutput(pvArgOutput, " ", 1);
545
546 cchWidth -= cchStr;
547 while (cchStr-- > 0)
548 {
549/**@todo #ifndef IN_RC*/
550#ifdef IN_RING3
551 char szUtf8[8]; /* Cp=0x7fffffff -> 6 bytes. */
552 char *pszEnd = RTStrPutCp(szUtf8, *puszStr++);
553 cch += pfnOutput(pvArgOutput, szUtf8, pszEnd - szUtf8);
554#else
555 char ch = (char)*puszStr++;
556 cch += pfnOutput(pvArgOutput, &ch, 1);
557#endif
558 }
559 while (--cchWidth >= 0)
560 cch += pfnOutput(pvArgOutput, " ", 1);
561 }
562 else
563 {
564 int cchStr;
565 const char *pszStr = va_arg(args, char*);
566
567 if (!VALID_PTR(pszStr))
568 pszStr = "<NULL>";
569 cchStr = _strnlen(pszStr, (unsigned)cchPrecision);
570 if (!(fFlags & RTSTR_F_LEFT))
571 while (--cchWidth >= cchStr)
572 cch += pfnOutput(pvArgOutput, " ", 1);
573
574 cch += pfnOutput(pvArgOutput, pszStr, cchStr);
575
576 while (--cchWidth >= cchStr)
577 cch += pfnOutput(pvArgOutput, " ", 1);
578 }
579 break;
580 }
581
582 /*-----------------*/
583 /* integer/pointer */
584 /*-----------------*/
585 case 'd':
586 case 'i':
587 case 'o':
588 case 'p':
589 case 'u':
590 case 'x':
591 case 'X':
592 {
593 char achNum[64]; /* FIXME */
594 int cchNum;
595 uint64_t u64Value;
596
597 switch (pszFormat[-1])
598 {
599 case 'd': /* signed decimal integer */
600 case 'i':
601 fFlags |= RTSTR_F_VALSIGNED;
602 break;
603
604 case 'o':
605 uBase = 8;
606 break;
607
608 case 'p':
609 fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
610 uBase = 16;
611 if (cchWidth < 0)
612 cchWidth = sizeof(char *) * 2;
613 break;
614
615 case 'u':
616 uBase = 10;
617 break;
618
619 case 'X':
620 fFlags |= RTSTR_F_CAPITAL;
621 case 'x':
622 uBase = 16;
623 break;
624 }
625
626 if (pszFormat[-1] == 'p')
627 u64Value = va_arg(args, uintptr_t);
628 else if (fFlags & RTSTR_F_VALSIGNED)
629 {
630 if (chArgSize == 'L')
631 {
632 u64Value = va_arg(args, int64_t);
633 fFlags |= RTSTR_F_64BIT;
634 }
635 else if (chArgSize == 'l')
636 {
637 u64Value = va_arg(args, signed long);
638 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
639 }
640 else if (chArgSize == 'h')
641 {
642 u64Value = va_arg(args, /* signed short */ int);
643 fFlags |= RTSTR_GET_BIT_FLAG(signed short);
644 }
645 else if (chArgSize == 'H')
646 {
647 u64Value = va_arg(args, /* int8_t */ int);
648 fFlags |= RTSTR_GET_BIT_FLAG(int8_t);
649 }
650 else if (chArgSize == 'j')
651 {
652 u64Value = va_arg(args, /*intmax_t*/ int64_t);
653 fFlags |= RTSTR_F_64BIT;
654 }
655 else if (chArgSize == 'z')
656 {
657 u64Value = va_arg(args, size_t);
658 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
659 }
660 else if (chArgSize == 't')
661 {
662 u64Value = va_arg(args, ptrdiff_t);
663 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
664 }
665 else
666 {
667 u64Value = va_arg(args, signed int);
668 fFlags |= RTSTR_GET_BIT_FLAG(signed int);
669 }
670 }
671 else
672 {
673 if (chArgSize == 'L')
674 {
675 u64Value = va_arg(args, uint64_t);
676 fFlags |= RTSTR_F_64BIT;
677 }
678 else if (chArgSize == 'l')
679 {
680 u64Value = va_arg(args, unsigned long);
681 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
682 }
683 else if (chArgSize == 'h')
684 {
685 u64Value = va_arg(args, /* unsigned short */ int);
686 fFlags |= RTSTR_GET_BIT_FLAG(unsigned short);
687 }
688 else if (chArgSize == 'H')
689 {
690 u64Value = va_arg(args, /* uint8_t */ int);
691 fFlags |= RTSTR_GET_BIT_FLAG(uint8_t);
692 }
693 else if (chArgSize == 'j')
694 {
695 u64Value = va_arg(args, /*uintmax_t*/ int64_t);
696 fFlags |= RTSTR_F_64BIT;
697 }
698 else if (chArgSize == 'z')
699 {
700 u64Value = va_arg(args, size_t);
701 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
702 }
703 else if (chArgSize == 't')
704 {
705 u64Value = va_arg(args, ptrdiff_t);
706 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
707 }
708 else
709 {
710 u64Value = va_arg(args, unsigned int);
711 fFlags |= RTSTR_GET_BIT_FLAG(unsigned int);
712 }
713 }
714 cchNum = RTStrFormatNumber((char *)SSToDS(&achNum), u64Value, uBase, cchWidth, cchPrecision, fFlags);
715 cch += pfnOutput(pvArgOutput, (char *)SSToDS(&achNum), cchNum);
716 break;
717 }
718
719 /*
720 * Nested extensions.
721 */
722 case 'M': /* replace the format string (not stacked yet). */
723 {
724 pszStartOutput = pszFormat = va_arg(args, const char *);
725 AssertPtr(pszStartOutput);
726 break;
727 }
728
729 case 'N': /* real nesting. */
730 {
731 const char *pszFormatNested = va_arg(args, const char *);
732 va_list *pArgsNested = va_arg(args, va_list *);
733 va_list ArgsNested;
734 va_copy(ArgsNested, *pArgsNested);
735 Assert(pszFormatNested);
736 cch += RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormatNested, ArgsNested);
737 va_end(ArgsNested);
738 break;
739 }
740
741 /*
742 * IPRT Extensions.
743 */
744 case 'R':
745 {
746 if (*pszFormat != '[')
747 {
748 pszFormat--;
749 cch += rtstrFormatRt(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
750 }
751 else
752 {
753 pszFormat--;
754 cch += rtstrFormatType(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
755 }
756 break;
757 }
758
759 /*
760 * Custom format.
761 */
762 default:
763 {
764 if (pfnFormat)
765 {
766 pszFormat--;
767 cch += pfnFormat(pvArgFormat, pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
768 }
769 break;
770 }
771 }
772 pszStartOutput = pszFormat;
773 }
774 }
775 else
776 pszFormat++;
777 }
778
779 /* output pending string. */
780 if (pszStartOutput != pszFormat)
781 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
782
783 /* terminate the output */
784 pfnOutput(pvArgOutput, NULL, 0);
785
786 return cch;
787}
788RT_EXPORT_SYMBOL(RTStrFormatV);
789
790
791/**
792 * Partial implementation of a printf like formatter.
793 * It doesn't do everything correct, and there is no floating point support.
794 * However, it supports custom formats by the means of a format callback.
795 *
796 * @returns number of bytes formatted.
797 * @param pfnOutput Output worker.
798 * Called in two ways. Normally with a string an it's length.
799 * For termination, it's called with NULL for string, 0 for length.
800 * @param pvArgOutput Argument to the output worker.
801 * @param pfnFormat Custom format worker.
802 * @param pvArgFormat Argument to the format worker.
803 * @param pszFormat Format string.
804 * @param ... Argument list.
805 */
806RTDECL(size_t) RTStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, const char *pszFormat, ...)
807{
808 size_t cch;
809 va_list args;
810 va_start(args, pszFormat);
811 cch = RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormat, args);
812 va_end(args);
813 return cch;
814}
815RT_EXPORT_SYMBOL(RTStrFormat);
816
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