VirtualBox

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

Last change on this file since 45221 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 29.9 KB
Line 
1/* $Id: strformat.cpp 44528 2013-02-04 14:27:54Z vboxsync $ */
2/** @file
3 * IPRT - String Formatter.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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 /*
454 * Argument size.
455 */
456 chArgSize = *pszFormat;
457 switch (chArgSize)
458 {
459 default:
460 chArgSize = 0;
461 break;
462
463 case 'z':
464 case 'L':
465 case 'j':
466 case 't':
467 pszFormat++;
468 break;
469
470 case 'l':
471 pszFormat++;
472 if (*pszFormat == 'l')
473 {
474 chArgSize = 'L';
475 pszFormat++;
476 }
477 break;
478
479 case 'h':
480 pszFormat++;
481 if (*pszFormat == 'h')
482 {
483 chArgSize = 'H';
484 pszFormat++;
485 }
486 break;
487
488 case 'I': /* Used by Win32/64 compilers. */
489 if ( pszFormat[1] == '6'
490 && pszFormat[2] == '4')
491 {
492 pszFormat += 3;
493 chArgSize = 'L';
494 }
495 else if ( pszFormat[1] == '3'
496 && pszFormat[2] == '2')
497 {
498 pszFormat += 3;
499 chArgSize = 0;
500 }
501 else
502 {
503 pszFormat += 1;
504 chArgSize = 'j';
505 }
506 break;
507
508 case 'q': /* Used on BSD platforms. */
509 pszFormat++;
510 chArgSize = 'L';
511 break;
512 }
513
514 /*
515 * The type.
516 */
517 switch (*pszFormat++)
518 {
519 /* char */
520 case 'c':
521 {
522 char ch;
523
524 if (!(fFlags & RTSTR_F_LEFT))
525 while (--cchWidth > 0)
526 cch += pfnOutput(pvArgOutput, " ", 1);
527
528 ch = (char)va_arg(args, int);
529 cch += pfnOutput(pvArgOutput, SSToDS(&ch), 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 szUtf8[8]; /* Cp=0x7fffffff -> 6 bytes. */
562 char *pszEnd = RTStrPutCp(szUtf8, Cp);
563 cch += pfnOutput(pvArgOutput, szUtf8, pszEnd - szUtf8);
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 szUtf8[8]; /* Cp=0x7fffffff -> 6 bytes. */
594 char *pszEnd = RTStrPutCp(szUtf8, *puszStr++);
595 cch += pfnOutput(pvArgOutput, szUtf8, pszEnd - szUtf8);
596#else
597 char ch = (char)*puszStr++;
598 cch += pfnOutput(pvArgOutput, &ch, 1);
599#endif
600 }
601 while (--cchWidth >= 0)
602 cch += pfnOutput(pvArgOutput, " ", 1);
603 }
604 else
605 {
606 int cchStr;
607 const char *pszStr = va_arg(args, char*);
608
609 if (!VALID_PTR(pszStr))
610 pszStr = "<NULL>";
611 cchStr = _strnlen(pszStr, (unsigned)cchPrecision);
612 if (!(fFlags & RTSTR_F_LEFT))
613 while (--cchWidth >= cchStr)
614 cch += pfnOutput(pvArgOutput, " ", 1);
615
616 cch += pfnOutput(pvArgOutput, pszStr, cchStr);
617
618 while (--cchWidth >= cchStr)
619 cch += pfnOutput(pvArgOutput, " ", 1);
620 }
621 break;
622 }
623
624 /*-----------------*/
625 /* integer/pointer */
626 /*-----------------*/
627 case 'd':
628 case 'i':
629 case 'o':
630 case 'p':
631 case 'u':
632 case 'x':
633 case 'X':
634 {
635 char achNum[64]; /* FIXME */
636 int cchNum;
637 uint64_t u64Value;
638
639 switch (pszFormat[-1])
640 {
641 case 'd': /* signed decimal integer */
642 case 'i':
643 fFlags |= RTSTR_F_VALSIGNED;
644 break;
645
646 case 'o':
647 uBase = 8;
648 break;
649
650 case 'p':
651 fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
652 uBase = 16;
653 if (cchWidth < 0)
654 cchWidth = sizeof(char *) * 2;
655 break;
656
657 case 'u':
658 uBase = 10;
659 break;
660
661 case 'X':
662 fFlags |= RTSTR_F_CAPITAL;
663 case 'x':
664 uBase = 16;
665 break;
666 }
667
668 if (pszFormat[-1] == 'p')
669 u64Value = va_arg(args, uintptr_t);
670 else if (fFlags & RTSTR_F_VALSIGNED)
671 {
672 if (chArgSize == 'L')
673 {
674 u64Value = va_arg(args, int64_t);
675 fFlags |= RTSTR_F_64BIT;
676 }
677 else if (chArgSize == 'l')
678 {
679 u64Value = va_arg(args, signed long);
680 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
681 }
682 else if (chArgSize == 'h')
683 {
684 u64Value = va_arg(args, /* signed short */ int);
685 fFlags |= RTSTR_GET_BIT_FLAG(signed short);
686 }
687 else if (chArgSize == 'H')
688 {
689 u64Value = va_arg(args, /* int8_t */ int);
690 fFlags |= RTSTR_GET_BIT_FLAG(int8_t);
691 }
692 else if (chArgSize == 'j')
693 {
694 u64Value = va_arg(args, /*intmax_t*/ int64_t);
695 fFlags |= RTSTR_F_64BIT;
696 }
697 else if (chArgSize == 'z')
698 {
699 u64Value = va_arg(args, size_t);
700 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
701 }
702 else if (chArgSize == 't')
703 {
704 u64Value = va_arg(args, ptrdiff_t);
705 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
706 }
707 else
708 {
709 u64Value = va_arg(args, signed int);
710 fFlags |= RTSTR_GET_BIT_FLAG(signed int);
711 }
712 }
713 else
714 {
715 if (chArgSize == 'L')
716 {
717 u64Value = va_arg(args, uint64_t);
718 fFlags |= RTSTR_F_64BIT;
719 }
720 else if (chArgSize == 'l')
721 {
722 u64Value = va_arg(args, unsigned long);
723 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
724 }
725 else if (chArgSize == 'h')
726 {
727 u64Value = va_arg(args, /* unsigned short */ int);
728 fFlags |= RTSTR_GET_BIT_FLAG(unsigned short);
729 }
730 else if (chArgSize == 'H')
731 {
732 u64Value = va_arg(args, /* uint8_t */ int);
733 fFlags |= RTSTR_GET_BIT_FLAG(uint8_t);
734 }
735 else if (chArgSize == 'j')
736 {
737 u64Value = va_arg(args, /*uintmax_t*/ int64_t);
738 fFlags |= RTSTR_F_64BIT;
739 }
740 else if (chArgSize == 'z')
741 {
742 u64Value = va_arg(args, size_t);
743 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
744 }
745 else if (chArgSize == 't')
746 {
747 u64Value = va_arg(args, ptrdiff_t);
748 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
749 }
750 else
751 {
752 u64Value = va_arg(args, unsigned int);
753 fFlags |= RTSTR_GET_BIT_FLAG(unsigned int);
754 }
755 }
756 cchNum = RTStrFormatNumber((char *)SSToDS(&achNum), u64Value, uBase, cchWidth, cchPrecision, fFlags);
757 cch += pfnOutput(pvArgOutput, (char *)SSToDS(&achNum), cchNum);
758 break;
759 }
760
761 /*
762 * Nested extensions.
763 */
764 case 'M': /* replace the format string (not stacked yet). */
765 {
766 pszStartOutput = pszFormat = va_arg(args, const char *);
767 AssertPtr(pszStartOutput);
768 break;
769 }
770
771 case 'N': /* real nesting. */
772 {
773 const char *pszFormatNested = va_arg(args, const char *);
774 va_list *pArgsNested = va_arg(args, va_list *);
775 va_list ArgsNested;
776 va_copy(ArgsNested, *pArgsNested);
777 Assert(pszFormatNested);
778 cch += RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormatNested, ArgsNested);
779 va_end(ArgsNested);
780 break;
781 }
782
783 /*
784 * IPRT Extensions.
785 */
786 case 'R':
787 {
788 if (*pszFormat != '[')
789 {
790 pszFormat--;
791 cch += rtstrFormatRt(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
792 }
793 else
794 {
795 pszFormat--;
796 cch += rtstrFormatType(pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
797 }
798 break;
799 }
800
801 /*
802 * Custom format.
803 */
804 default:
805 {
806 if (pfnFormat)
807 {
808 pszFormat--;
809 cch += pfnFormat(pvArgFormat, pfnOutput, pvArgOutput, &pszFormat, &args, cchWidth, cchPrecision, fFlags, chArgSize);
810 }
811 break;
812 }
813 }
814 pszStartOutput = pszFormat;
815 }
816 }
817 else
818 pszFormat++;
819 }
820
821 /* output pending string. */
822 if (pszStartOutput != pszFormat)
823 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
824
825 /* terminate the output */
826 pfnOutput(pvArgOutput, NULL, 0);
827
828 return cch;
829}
830RT_EXPORT_SYMBOL(RTStrFormatV);
831
832
833/**
834 * Partial implementation of a printf like formatter.
835 * It doesn't do everything correct, and there is no floating point support.
836 * However, it supports custom formats by the means of a format callback.
837 *
838 * @returns number of bytes formatted.
839 * @param pfnOutput Output worker.
840 * Called in two ways. Normally with a string an it's length.
841 * For termination, it's called with NULL for string, 0 for length.
842 * @param pvArgOutput Argument to the output worker.
843 * @param pfnFormat Custom format worker.
844 * @param pvArgFormat Argument to the format worker.
845 * @param pszFormat Format string.
846 * @param ... Argument list.
847 */
848RTDECL(size_t) RTStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, const char *pszFormat, ...)
849{
850 size_t cch;
851 va_list args;
852 va_start(args, pszFormat);
853 cch = RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormat, args);
854 va_end(args);
855 return cch;
856}
857RT_EXPORT_SYMBOL(RTStrFormat);
858
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