VirtualBox

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

Last change on this file since 29660 was 29660, checked in by vboxsync, 15 years ago

IPRT: Fixed number formatting bug where we would pad with '0' instead of ' '.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 32.7 KB
Line 
1/* $Id: strformat.cpp 29660 2010-05-19 14:29:22Z 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.215389.xyz. 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, unsigned int fFlags)
151{
152 return rtStrFormatNumber(psz, *(KSIZE64 *)(void *)&u64Value, uiBase, cchWidth, cchPrecision, fFlags);
153}
154RT_EXPORT_SYMBOL(RTStrFormatNumber);
155
156
157
158/**
159 * Formats an integer number according to the parameters.
160 *
161 * @returns Length of the number.
162 * @param psz Pointer to output string.
163 * @param ullValue Value. Using the high part is optional.
164 * @param uiBase Number representation base.
165 * @param cchWidth Width
166 * @param cchPrecision Precision.
167 * @param fFlags Flags (NTFS_*).
168 */
169static int rtStrFormatNumber(char *psz, KSIZE64 ullValue, unsigned int uiBase, signed int cchWidth, signed int cchPrecision, unsigned int fFlags)
170{
171 const char *pachDigits = "0123456789abcdef";
172 char *pszStart = psz;
173 int cchValue;
174 unsigned long ul;
175 int i;
176 int j;
177
178 /*
179 * Validate and adjust input...
180 */
181 Assert(uiBase >= 2 || uiBase <= 16);
182 if (fFlags & RTSTR_F_CAPITAL)
183 pachDigits = "0123456789ABCDEF";
184 if (fFlags & RTSTR_F_LEFT)
185 fFlags &= ~RTSTR_F_ZEROPAD;
186 if ( (fFlags & RTSTR_F_THOUSAND_SEP)
187 && ( uiBase != 10
188 || (fFlags & RTSTR_F_ZEROPAD))) /** @todo implement RTSTR_F_ZEROPAD + RTSTR_F_THOUSAND_SEP. */
189 fFlags &= ~RTSTR_F_THOUSAND_SEP;
190
191 /*
192 * Determin value length
193 */
194 cchValue = 0;
195 if (ullValue.ulHi || (fFlags & RTSTR_F_64BIT))
196 {
197 uint64_t u64 = *(uint64_t *)(void *)&ullValue;
198 if ((fFlags & RTSTR_F_VALSIGNED) && (ullValue.ulHi & 0x80000000))
199 u64 = -(int64_t)u64;
200 do
201 {
202 cchValue++;
203 u64 /= uiBase;
204 } while (u64);
205 }
206 else
207 {
208 ul = (fFlags & RTSTR_F_VALSIGNED) && (ullValue.ulLo & 0x80000000) ? -(int32_t)ullValue.ulLo : ullValue.ulLo;
209 do
210 {
211 cchValue++;
212 ul /= uiBase;
213 } while (ul);
214 }
215 if (fFlags & RTSTR_F_THOUSAND_SEP)
216 {
217 if (cchValue <= 3)
218 fFlags &= ~RTSTR_F_THOUSAND_SEP;
219 else
220 cchValue += cchValue / 3 - (cchValue % 3 == 0);
221 }
222
223 /*
224 * Sign (+/-).
225 */
226 i = 0;
227 if (fFlags & RTSTR_F_VALSIGNED)
228 {
229 if ((ullValue.ulHi || (fFlags & RTSTR_F_64BIT) ? ullValue.ulHi : ullValue.ulLo) & 0x80000000)
230 {
231 ullValue.ulLo = -(int32_t)ullValue.ulLo;
232 if (ullValue.ulHi)
233 ullValue.ulHi = ~ullValue.ulHi;
234 psz[i++] = '-';
235 }
236 else if (fFlags & (RTSTR_F_PLUS | RTSTR_F_BLANK))
237 psz[i++] = (char)(fFlags & RTSTR_F_PLUS ? '+' : ' ');
238 }
239
240 /*
241 * Special (0/0x).
242 */
243 if ((fFlags & RTSTR_F_SPECIAL) && (uiBase % 8) == 0)
244 {
245 psz[i++] = '0';
246 if (uiBase == 16)
247 psz[i++] = (char)(fFlags & RTSTR_F_CAPITAL ? 'X' : 'x');
248 }
249
250 /*
251 * width - only if ZEROPAD
252 */
253 cchWidth -= i + cchValue;
254 if (fFlags & RTSTR_F_ZEROPAD)
255 while (--cchWidth >= 0)
256 {
257 psz[i++] = '0';
258 cchPrecision--;
259 }
260 else if (!(fFlags & RTSTR_F_LEFT) && cchWidth > 0)
261 {
262 for (j = i-1; j >= 0; j--)
263 psz[cchWidth + j] = psz[j];
264 for (j = 0; j < cchWidth; j++)
265 psz[j] = ' ';
266 i += cchWidth;
267 }
268 psz += i;
269
270
271 /*
272 * precision
273 */
274 while (--cchPrecision >= cchValue)
275 *psz++ = ' ';
276
277 /*
278 * write number - not good enough but it works
279 */
280 psz += cchValue;
281 i = -1;
282 if (ullValue.ulHi || (fFlags & RTSTR_F_64BIT))
283 {
284 uint64_t u64 = *(uint64_t *)(void *)&ullValue;
285 if (fFlags & RTSTR_F_THOUSAND_SEP)
286 {
287 do
288 {
289 if ((-i - 1) % 4 == 3)
290 psz[i--] = ' ';
291 psz[i--] = pachDigits[u64 % uiBase];
292 u64 /= uiBase;
293 } while (u64);
294 }
295 else
296 {
297 do
298 {
299 psz[i--] = pachDigits[u64 % uiBase];
300 u64 /= uiBase;
301 } while (u64);
302 }
303 }
304 else
305 {
306 ul = (fFlags & RTSTR_F_VALSIGNED) && (ullValue.ulLo & 0x80000000) ? -(int32_t)ullValue.ulLo : ullValue.ulLo;
307 if (fFlags & RTSTR_F_THOUSAND_SEP)
308 {
309 do
310 {
311 if ((-i - 1) % 4 == 3)
312 psz[i--] = ' ';
313 psz[i--] = pachDigits[ul % uiBase];
314 ul /= uiBase;
315 } while (ul);
316 }
317 else
318 {
319 do
320 {
321 psz[i--] = pachDigits[ul % uiBase];
322 ul /= uiBase;
323 } while (ul);
324 }
325 }
326
327 /*
328 * width if RTSTR_F_LEFT
329 */
330 if (fFlags & RTSTR_F_LEFT)
331 while (--cchWidth >= 0)
332 *psz++ = ' ';
333
334 *psz = '\0';
335 return (unsigned)(psz - pszStart);
336}
337
338
339/**
340 * Partial implementation of a printf like formatter.
341 * It doesn't do everything correct, and there is no floating point support.
342 * However, it supports custom formats by the means of a format callback.
343 *
344 * @returns number of bytes formatted.
345 * @param pfnOutput Output worker.
346 * Called in two ways. Normally with a string an it's length.
347 * For termination, it's called with NULL for string, 0 for length.
348 * @param pvArgOutput Argument to the output worker.
349 * @param pfnFormat Custom format worker.
350 * @param pvArgFormat Argument to the format worker.
351 * @param pszFormat Format string.
352 * @param InArgs Argument list.
353 */
354RTDECL(size_t) RTStrFormatV(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, const char *pszFormat, va_list InArgs)
355{
356 va_list args;
357 KSIZE cch = 0;
358 const char *pszStartOutput = pszFormat;
359
360 va_copy(args, InArgs); /* make a copy so we can reference it (AMD64 / gcc). */
361
362 while (*pszFormat != '\0')
363 {
364 if (*pszFormat == '%')
365 {
366 /* output pending string. */
367 if (pszStartOutput != pszFormat)
368 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
369
370 /* skip '%' */
371 pszFormat++;
372 if (*pszFormat == '%') /* '%%'-> '%' */
373 pszStartOutput = pszFormat++;
374 else
375 {
376 unsigned int fFlags = 0;
377 int cchWidth = -1;
378 int cchPrecision = -1;
379 unsigned int uBase = 10;
380 char chArgSize;
381
382 /* flags */
383 for (;;)
384 {
385 switch (*pszFormat++)
386 {
387 case '#': fFlags |= RTSTR_F_SPECIAL; continue;
388 case '-': fFlags |= RTSTR_F_LEFT; continue;
389 case '+': fFlags |= RTSTR_F_PLUS; continue;
390 case ' ': fFlags |= RTSTR_F_BLANK; continue;
391 case '0': fFlags |= RTSTR_F_ZEROPAD; continue;
392 case '\'': fFlags |= RTSTR_F_THOUSAND_SEP; continue;
393 }
394 pszFormat--;
395 break;
396 }
397
398 /* width */
399 if (ISDIGIT(*pszFormat))
400 {
401 for (cchWidth = 0; ISDIGIT(*pszFormat); pszFormat++)
402 {
403 cchWidth *= 10;
404 cchWidth += *pszFormat - '0';
405 }
406 fFlags |= RTSTR_F_WIDTH;
407 }
408 else if (*pszFormat == '*')
409 {
410 pszFormat++;
411 cchWidth = va_arg(args, int);
412 if (cchWidth < 0)
413 {
414 cchWidth = -cchWidth;
415 fFlags |= RTSTR_F_LEFT;
416 }
417 fFlags |= RTSTR_F_WIDTH;
418 }
419
420 /* precision */
421 if (*pszFormat == '.')
422 {
423 pszFormat++;
424 if (ISDIGIT(*pszFormat))
425 {
426 for (cchPrecision = 0; ISDIGIT(*pszFormat); pszFormat++)
427 {
428 cchPrecision *= 10;
429 cchPrecision += *pszFormat - '0';
430 }
431
432 }
433 else if (*pszFormat == '*')
434 {
435 pszFormat++;
436 cchPrecision = va_arg(args, int);
437 }
438 if (cchPrecision < 0)
439 cchPrecision = 0;
440 fFlags |= RTSTR_F_PRECISION;
441 }
442
443 /* argsize */
444 chArgSize = *pszFormat;
445 if (chArgSize != 'l' && chArgSize != 'L' && chArgSize != 'h' && chArgSize != 'j' && chArgSize != 'z' && chArgSize != 't')
446 chArgSize = 0;
447 else
448 {
449 pszFormat++;
450 if (*pszFormat == 'l' && chArgSize == 'l')
451 {
452 chArgSize = 'L';
453 pszFormat++;
454 }
455 else if (*pszFormat == 'h' && chArgSize == 'h')
456 {
457 chArgSize = 'H';
458 pszFormat++;
459 }
460 }
461
462 /*
463 * The type.
464 */
465 switch (*pszFormat++)
466 {
467 /* char */
468 case 'c':
469 {
470 char ch;
471
472 if (!(fFlags & RTSTR_F_LEFT))
473 while (--cchWidth > 0)
474 cch += pfnOutput(pvArgOutput, " ", 1);
475
476 ch = (char)va_arg(args, int);
477 cch += pfnOutput(pvArgOutput, SSToDS(&ch), 1);
478
479 while (--cchWidth > 0)
480 cch += pfnOutput(pvArgOutput, " ", 1);
481 break;
482 }
483
484#ifndef IN_RING3
485 case 'S': /* Unicode string as current code page -> Unicode as UTF-8 in GC/R0. */
486 chArgSize = 'l';
487 /* fall thru */
488#endif
489 case 's': /* Unicode string as utf8 */
490 {
491 if (chArgSize == 'l')
492 {
493 /* utf-16 -> utf-8 */
494 int cchStr;
495 PCRTUTF16 pwszStr = va_arg(args, PRTUTF16);
496
497 if (!VALID_PTR(pwszStr))
498 {
499 static RTUTF16 s_wszNull[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
500 pwszStr = s_wszNull;
501 }
502 cchStr = _strnlenUtf16(pwszStr, (unsigned)cchPrecision);
503 if (!(fFlags & RTSTR_F_LEFT))
504 while (--cchWidth >= cchStr)
505 cch += pfnOutput(pvArgOutput, " ", 1);
506 cchWidth -= cchStr;
507 while (cchStr-- > 0)
508 {
509#ifdef IN_RING3
510 RTUNICP Cp;
511 RTUtf16GetCpEx(&pwszStr, &Cp);
512 char szUtf8[8]; /* Cp=0x7fffffff -> 6 bytes. */
513 char *pszEnd = RTStrPutCp(szUtf8, Cp);
514 cch += pfnOutput(pvArgOutput, szUtf8, pszEnd - szUtf8);
515#else
516 char ch = (char)*pwszStr++;
517 cch += pfnOutput(pvArgOutput, &ch, 1);
518#endif
519 }
520 while (--cchWidth >= 0)
521 cch += pfnOutput(pvArgOutput, " ", 1);
522 }
523 else if (chArgSize == 'L')
524 {
525 /* unicp -> utf8 */
526 int cchStr;
527 PCRTUNICP puszStr = va_arg(args, PCRTUNICP);
528
529 if (!VALID_PTR(puszStr))
530 {
531 static RTUNICP s_uszNull[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
532 puszStr = s_uszNull;
533 }
534 cchStr = _strnlenUni(puszStr, (unsigned)cchPrecision);
535 if (!(fFlags & RTSTR_F_LEFT))
536 while (--cchWidth >= cchStr)
537 cch += pfnOutput(pvArgOutput, " ", 1);
538
539 cchWidth -= cchStr;
540 while (cchStr-- > 0)
541 {
542#ifdef IN_RING3
543 char szUtf8[8]; /* Cp=0x7fffffff -> 6 bytes. */
544 char *pszEnd = RTStrPutCp(szUtf8, *puszStr++);
545 cch += pfnOutput(pvArgOutput, szUtf8, pszEnd - szUtf8);
546#else
547 char ch = (char)*puszStr++;
548 cch += pfnOutput(pvArgOutput, &ch, 1);
549#endif
550 }
551 while (--cchWidth >= 0)
552 cch += pfnOutput(pvArgOutput, " ", 1);
553 }
554 else
555 {
556 int cchStr;
557 const char *pszStr = va_arg(args, char*);
558
559 if (!VALID_PTR(pszStr))
560 pszStr = "<NULL>";
561 cchStr = _strnlen(pszStr, (unsigned)cchPrecision);
562 if (!(fFlags & RTSTR_F_LEFT))
563 while (--cchWidth >= cchStr)
564 cch += pfnOutput(pvArgOutput, " ", 1);
565
566 cch += pfnOutput(pvArgOutput, pszStr, cchStr);
567
568 while (--cchWidth >= cchStr)
569 cch += pfnOutput(pvArgOutput, " ", 1);
570 }
571 break;
572 }
573
574#ifdef IN_RING3
575 case 'S': /* Unicode string as current code page. */
576 {
577 if (chArgSize == 'l')
578 {
579 /* UTF-16 */
580 int cchStr;
581 PCRTUTF16 pwsz2Str = va_arg(args, PRTUTF16);
582 if (!VALID_PTR(pwsz2Str))
583 {
584 static RTUTF16 s_wsz2Null[] = {'<', 'N', 'U', 'L', 'L', '>', '\0' };
585 pwsz2Str = s_wsz2Null;
586 }
587
588 cchStr = _strnlenUtf16(pwsz2Str, (unsigned)cchPrecision);
589 if (!(fFlags & RTSTR_F_LEFT))
590 while (--cchWidth >= cchStr)
591 cch += pfnOutput(pvArgOutput, " ", 1);
592
593 if (cchStr)
594 {
595 /* allocate temporary buffer. */
596 PRTUTF16 pwsz2Tmp = (PRTUTF16)RTMemTmpAlloc((cchStr + 1) * sizeof(RTUTF16));
597 memcpy(pwsz2Tmp, pwsz2Str, cchStr * sizeof(RTUTF16));
598 pwsz2Tmp[cchStr] = '\0';
599
600 char *pszUtf8;
601 int rc = RTUtf16ToUtf8(pwsz2Tmp, &pszUtf8);
602 if (RT_SUCCESS(rc))
603 {
604 char *pszCurCp;
605 rc = RTStrUtf8ToCurrentCP(&pszCurCp, pszUtf8);
606 if (RT_SUCCESS(rc))
607 {
608 cch += pfnOutput(pvArgOutput, pszCurCp, strlen(pszCurCp));
609 RTStrFree(pszCurCp);
610 }
611 RTStrFree(pszUtf8);
612 }
613 if (RT_FAILURE(rc))
614 while (cchStr-- > 0)
615 cch += pfnOutput(pvArgOutput, "\x7f", 1);
616 RTMemTmpFree(pwsz2Tmp);
617 }
618
619 while (--cchWidth >= cchStr)
620 cch += pfnOutput(pvArgOutput, " ", 1);
621 }
622 else if (chArgSize == 'L')
623 {
624 /* UCS-32 */
625 AssertMsgFailed(("Not implemented yet\n"));
626 }
627 else
628 {
629 /* UTF-8 */
630 int cchStr;
631 const char *pszStr = va_arg(args, char *);
632
633 if (!VALID_PTR(pszStr))
634 pszStr = "<NULL>";
635 cchStr = _strnlen(pszStr, (unsigned)cchPrecision);
636 if (!(fFlags & RTSTR_F_LEFT))
637 while (--cchWidth >= cchStr)
638 cch += pfnOutput(pvArgOutput, " ", 1);
639
640 if (cchStr)
641 {
642 /* allocate temporary buffer. */
643 char *pszTmp = (char *)RTMemTmpAlloc(cchStr + 1);
644 memcpy(pszTmp, pszStr, cchStr);
645 pszTmp[cchStr] = '\0';
646
647 char *pszCurCp;
648 int rc = RTStrUtf8ToCurrentCP(&pszCurCp, pszTmp);
649 if (RT_SUCCESS(rc))
650 {
651 cch += pfnOutput(pvArgOutput, pszCurCp, strlen(pszCurCp));
652 RTStrFree(pszCurCp);
653 }
654 else
655 while (cchStr-- > 0)
656 cch += pfnOutput(pvArgOutput, "\x7f", 1);
657 RTMemTmpFree(pszTmp);
658 }
659
660 while (--cchWidth >= cchStr)
661 cch += pfnOutput(pvArgOutput, " ", 1);
662 }
663 break;
664 }
665#endif
666
667
668 /*-----------------*/
669 /* integer/pointer */
670 /*-----------------*/
671 case 'd':
672 case 'i':
673 case 'o':
674 case 'p':
675 case 'u':
676 case 'x':
677 case 'X':
678 {
679 char achNum[64]; /* FIXME */
680 int cchNum;
681 uint64_t u64Value;
682
683 switch (pszFormat[-1])
684 {
685 case 'd': /* signed decimal integer */
686 case 'i':
687 fFlags |= RTSTR_F_VALSIGNED;
688 break;
689
690 case 'o':
691 uBase = 8;
692 break;
693
694 case 'p':
695 fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
696 uBase = 16;
697 if (cchWidth < 0)
698 cchWidth = sizeof(char *) * 2;
699 break;
700
701 case 'u':
702 uBase = 10;
703 break;
704
705 case 'X':
706 fFlags |= RTSTR_F_CAPITAL;
707 case 'x':
708 uBase = 16;
709 break;
710 }
711
712 if (pszFormat[-1] == 'p')
713 u64Value = va_arg(args, uintptr_t);
714 else if (fFlags & RTSTR_F_VALSIGNED)
715 {
716 if (chArgSize == 'L')
717 {
718 u64Value = va_arg(args, int64_t);
719 fFlags |= RTSTR_F_64BIT;
720 }
721 else if (chArgSize == 'l')
722 {
723 u64Value = va_arg(args, signed long);
724 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
725 }
726 else if (chArgSize == 'h')
727 {
728 u64Value = va_arg(args, /* signed short */ int);
729 fFlags |= RTSTR_GET_BIT_FLAG(signed short);
730 }
731 else if (chArgSize == 'H')
732 {
733 u64Value = va_arg(args, /* int8_t */ int);
734 fFlags |= RTSTR_GET_BIT_FLAG(int8_t);
735 }
736 else if (chArgSize == 'j')
737 {
738 u64Value = va_arg(args, /*intmax_t*/ int64_t);
739 fFlags |= RTSTR_F_64BIT;
740 }
741 else if (chArgSize == 'z')
742 {
743 u64Value = va_arg(args, size_t);
744 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
745 }
746 else if (chArgSize == 't')
747 {
748 u64Value = va_arg(args, ptrdiff_t);
749 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
750 }
751 else
752 {
753 u64Value = va_arg(args, signed int);
754 fFlags |= RTSTR_GET_BIT_FLAG(signed int);
755 }
756 }
757 else
758 {
759 if (chArgSize == 'L')
760 {
761 u64Value = va_arg(args, uint64_t);
762 fFlags |= RTSTR_F_64BIT;
763 }
764 else if (chArgSize == 'l')
765 {
766 u64Value = va_arg(args, unsigned long);
767 fFlags |= RTSTR_GET_BIT_FLAG(unsigned long);
768 }
769 else if (chArgSize == 'h')
770 {
771 u64Value = va_arg(args, /* unsigned short */ int);
772 fFlags |= RTSTR_GET_BIT_FLAG(unsigned short);
773 }
774 else if (chArgSize == 'H')
775 {
776 u64Value = va_arg(args, /* uint8_t */ int);
777 fFlags |= RTSTR_GET_BIT_FLAG(uint8_t);
778 }
779 else if (chArgSize == 'j')
780 {
781 u64Value = va_arg(args, /*uintmax_t*/ int64_t);
782 fFlags |= RTSTR_F_64BIT;
783 }
784 else if (chArgSize == 'z')
785 {
786 u64Value = va_arg(args, size_t);
787 fFlags |= RTSTR_GET_BIT_FLAG(size_t);
788 }
789 else if (chArgSize == 't')
790 {
791 u64Value = va_arg(args, ptrdiff_t);
792 fFlags |= RTSTR_GET_BIT_FLAG(ptrdiff_t);
793 }
794 else
795 {
796 u64Value = va_arg(args, unsigned int);
797 fFlags |= RTSTR_GET_BIT_FLAG(unsigned int);
798 }
799 }
800 cchNum = RTStrFormatNumber((char *)SSToDS(&achNum), u64Value, uBase, cchWidth, cchPrecision, fFlags);
801 cch += pfnOutput(pvArgOutput, (char *)SSToDS(&achNum), cchNum);
802 break;
803 }
804
805 /*
806 * Nested extensions.
807 */
808 case 'M': /* replace the format string (not stacked yet). */
809 {
810 pszStartOutput = pszFormat = va_arg(args, const char *);
811 AssertPtr(pszStartOutput);
812 break;
813 }
814
815 case 'N': /* real nesting. */
816 {
817 const char *pszFormatNested = va_arg(args, const char *);
818 va_list *pArgsNested = va_arg(args, va_list *);
819 va_list ArgsNested;
820 va_copy(ArgsNested, *pArgsNested);
821 Assert(pszFormatNested);
822 cch += RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormatNested, ArgsNested);
823 va_end(ArgsNested);
824 break;
825 }
826
827 /*
828 * IPRT Extensions.
829 */
830 case 'R':
831 {
832 if (*pszFormat != '[')
833 {
834 pszFormat--;
835 cch += rtstrFormatRt(pfnOutput, pvArgOutput, &pszFormat, &args, cchPrecision, cchWidth, fFlags, chArgSize);
836 }
837 else
838 {
839 pszFormat--;
840 cch += rtstrFormatType(pfnOutput, pvArgOutput, &pszFormat, &args, cchPrecision, cchWidth, fFlags, chArgSize);
841 }
842 break;
843 }
844
845 /*
846 * Custom format.
847 */
848 default:
849 {
850 if (pfnFormat)
851 {
852 pszFormat--;
853 cch += pfnFormat(pvArgFormat, pfnOutput, pvArgOutput, &pszFormat, &args, cchPrecision, cchWidth, fFlags, chArgSize);
854 }
855 break;
856 }
857 }
858 pszStartOutput = pszFormat;
859 }
860 }
861 else
862 pszFormat++;
863 }
864
865 /* output pending string. */
866 if (pszStartOutput != pszFormat)
867 cch += pfnOutput(pvArgOutput, pszStartOutput, pszFormat - pszStartOutput);
868
869 /* terminate the output */
870 pfnOutput(pvArgOutput, NULL, 0);
871
872 return cch;
873}
874RT_EXPORT_SYMBOL(RTStrFormatV);
875
876
877/**
878 * Partial implementation of a printf like formatter.
879 * It doesn't do everything correct, and there is no floating point support.
880 * However, it supports custom formats by the means of a format callback.
881 *
882 * @returns number of bytes formatted.
883 * @param pfnOutput Output worker.
884 * Called in two ways. Normally with a string an it's length.
885 * For termination, it's called with NULL for string, 0 for length.
886 * @param pvArgOutput Argument to the output worker.
887 * @param pfnFormat Custom format worker.
888 * @param pvArgFormat Argument to the format worker.
889 * @param pszFormat Format string.
890 * @param ... Argument list.
891 */
892RTDECL(size_t) RTStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PFNSTRFORMAT pfnFormat, void *pvArgFormat, const char *pszFormat, ...)
893{
894 size_t cch;
895 va_list args;
896 va_start(args, pszFormat);
897 cch = RTStrFormatV(pfnOutput, pvArgOutput, pfnFormat, pvArgFormat, pszFormat, args);
898 va_end(args);
899 return cch;
900}
901RT_EXPORT_SYMBOL(RTStrFormat);
902
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette