VirtualBox

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

Last change on this file since 20273 was 19942, checked in by vboxsync, 16 years ago

IPRT: Implemented thousand separators for the string formatting code and RTStrFormatNumber. (For instance %'u.)

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