VirtualBox

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

Last change on this file since 74157 was 74157, checked in by vboxsync, 6 years ago

IPRT/strformat: Fixed bug formatting INT64_MAX. Cleaned up the integer formatting code to get rid of anachronisms (VAC308).

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