VirtualBox

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

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

iprt/string.h: Dropped including utf16.h and let those who need it include it themselves. bugref:9344

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